hazo_collab_forms 5.7.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (362) hide show
  1. package/CHANGE_LOG.md +115 -0
  2. package/README.md +203 -111
  3. package/dist/audit/built_in_actions.d.ts +6 -0
  4. package/dist/audit/built_in_actions.d.ts.map +1 -0
  5. package/dist/audit/built_in_actions.js +23 -0
  6. package/dist/audit/built_in_actions.js.map +1 -0
  7. package/dist/audit/intent_emitter.d.ts +4 -0
  8. package/dist/audit/intent_emitter.d.ts.map +1 -0
  9. package/dist/audit/intent_emitter.js +25 -0
  10. package/dist/audit/intent_emitter.js.map +1 -0
  11. package/dist/audit/lazy_audit_icon.d.ts +8 -0
  12. package/dist/audit/lazy_audit_icon.d.ts.map +1 -0
  13. package/dist/audit/lazy_audit_icon.js +20 -0
  14. package/dist/audit/lazy_audit_icon.js.map +1 -0
  15. package/dist/components/_internal_form_set.d.ts +6 -0
  16. package/dist/components/_internal_form_set.d.ts.map +1 -1
  17. package/dist/components/_internal_form_set.js +48 -51
  18. package/dist/components/_internal_form_set.js.map +1 -1
  19. package/dist/components/clarification/clarification_item_body.d.ts +1 -1
  20. package/dist/components/clarification/clarification_item_body.d.ts.map +1 -1
  21. package/dist/components/clarification/resolution_status_strip.d.ts +1 -1
  22. package/dist/components/clarification/resolution_status_strip.d.ts.map +1 -1
  23. package/dist/components/field_audit/auditor.d.ts +30 -0
  24. package/dist/components/field_audit/auditor.d.ts.map +1 -0
  25. package/dist/components/field_audit/auditor.js +91 -0
  26. package/dist/components/field_audit/auditor.js.map +1 -0
  27. package/dist/components/field_audit/context.d.ts +29 -0
  28. package/dist/components/field_audit/context.d.ts.map +1 -0
  29. package/dist/components/field_audit/context.js +123 -0
  30. package/dist/components/field_audit/context.js.map +1 -0
  31. package/dist/components/field_audit/field_audit_icon.d.ts +12 -0
  32. package/dist/components/field_audit/field_audit_icon.d.ts.map +1 -0
  33. package/dist/components/field_audit/field_audit_icon.js +23 -0
  34. package/dist/components/field_audit/field_audit_icon.js.map +1 -0
  35. package/dist/components/field_audit/field_audit_panel.d.ts +9 -0
  36. package/dist/components/field_audit/field_audit_panel.d.ts.map +1 -0
  37. package/dist/components/field_audit/field_audit_panel.js +54 -0
  38. package/dist/components/field_audit/field_audit_panel.js.map +1 -0
  39. package/dist/components/field_audit/index.d.ts +33 -0
  40. package/dist/components/field_audit/index.d.ts.map +1 -0
  41. package/dist/components/field_audit/index.js +29 -0
  42. package/dist/components/field_audit/index.js.map +1 -0
  43. package/dist/components/field_audit/types.d.ts +75 -0
  44. package/dist/components/field_audit/types.d.ts.map +1 -0
  45. package/dist/components/field_audit/types.js +10 -0
  46. package/dist/components/field_audit/types.js.map +1 -0
  47. package/dist/components/field_audit/with_field_audit.d.ts +32 -0
  48. package/dist/components/field_audit/with_field_audit.d.ts.map +1 -0
  49. package/dist/components/field_audit/with_field_audit.js +42 -0
  50. package/dist/components/field_audit/with_field_audit.js.map +1 -0
  51. package/dist/components/hazo_collab_form_checkbox.d.ts.map +1 -1
  52. package/dist/components/hazo_collab_form_checkbox.js +3 -1
  53. package/dist/components/hazo_collab_form_checkbox.js.map +1 -1
  54. package/dist/components/hazo_collab_form_doc.d.ts.map +1 -1
  55. package/dist/components/hazo_collab_form_doc.js +4 -1
  56. package/dist/components/hazo_collab_form_doc.js.map +1 -1
  57. package/dist/components/hazo_collab_form_radio.d.ts.map +1 -1
  58. package/dist/components/hazo_collab_form_radio.js +4 -2
  59. package/dist/components/hazo_collab_form_radio.js.map +1 -1
  60. package/dist/components/hazo_collab_form_view/context.d.ts +7 -0
  61. package/dist/components/hazo_collab_form_view/context.d.ts.map +1 -1
  62. package/dist/components/hazo_collab_form_view/context.js +46 -0
  63. package/dist/components/hazo_collab_form_view/context.js.map +1 -1
  64. package/dist/components/hazo_collab_form_view/hooks/use_view_callbacks.d.ts +8 -1
  65. package/dist/components/hazo_collab_form_view/hooks/use_view_callbacks.d.ts.map +1 -1
  66. package/dist/components/hazo_collab_form_view/hooks/use_view_callbacks.js +4 -2
  67. package/dist/components/hazo_collab_form_view/hooks/use_view_callbacks.js.map +1 -1
  68. package/dist/components/hazo_collab_form_view/index.d.ts +1 -1
  69. package/dist/components/hazo_collab_form_view/index.d.ts.map +1 -1
  70. package/dist/components/hazo_collab_form_view/index.js +59 -3
  71. package/dist/components/hazo_collab_form_view/index.js.map +1 -1
  72. package/dist/components/hazo_collab_form_view/types.d.ts +134 -0
  73. package/dist/components/hazo_collab_form_view/types.d.ts.map +1 -1
  74. package/dist/components/hazo_collab_form_view/views/approval_view.d.ts.map +1 -1
  75. package/dist/components/hazo_collab_form_view/views/approval_view.js +3 -1
  76. package/dist/components/hazo_collab_form_view/views/approval_view.js.map +1 -1
  77. package/dist/components/hazo_collab_form_view/views/edit_view.d.ts.map +1 -1
  78. package/dist/components/hazo_collab_form_view/views/edit_view.js +8 -3
  79. package/dist/components/hazo_collab_form_view/views/edit_view.js.map +1 -1
  80. package/dist/components/hazo_collab_form_view/views/print_view.d.ts.map +1 -1
  81. package/dist/components/hazo_collab_form_view/views/print_view.js +3 -1
  82. package/dist/components/hazo_collab_form_view/views/print_view.js.map +1 -1
  83. package/dist/components/hazo_collab_form_view/views/summary_view.d.ts.map +1 -1
  84. package/dist/components/hazo_collab_form_view/views/summary_view.js +4 -2
  85. package/dist/components/hazo_collab_form_view/views/summary_view.js.map +1 -1
  86. package/dist/components/hazo_data_form/group_renderer.d.ts +8 -2
  87. package/dist/components/hazo_data_form/group_renderer.d.ts.map +1 -1
  88. package/dist/components/hazo_data_form/group_renderer.js +3 -3
  89. package/dist/components/hazo_data_form/group_renderer.js.map +1 -1
  90. package/dist/components/hazo_data_form/hazo_data_form.d.ts +2 -1
  91. package/dist/components/hazo_data_form/hazo_data_form.d.ts.map +1 -1
  92. package/dist/components/hazo_data_form/hazo_data_form.js +47 -6
  93. package/dist/components/hazo_data_form/hazo_data_form.js.map +1 -1
  94. package/dist/components/hazo_data_form/section_renderer.d.ts +4 -2
  95. package/dist/components/hazo_data_form/section_renderer.d.ts.map +1 -1
  96. package/dist/components/hazo_data_form/section_renderer.js +2 -2
  97. package/dist/components/hazo_data_form/section_renderer.js.map +1 -1
  98. package/dist/components/hazo_data_form/shared/data_form_field_layout.d.ts +4 -1
  99. package/dist/components/hazo_data_form/shared/data_form_field_layout.d.ts.map +1 -1
  100. package/dist/components/hazo_data_form/shared/data_form_field_layout.js +34 -8
  101. package/dist/components/hazo_data_form/shared/data_form_field_layout.js.map +1 -1
  102. package/dist/components/hazo_data_form/types.d.ts +56 -1
  103. package/dist/components/hazo_data_form/types.d.ts.map +1 -1
  104. package/dist/components/index.d.ts +5 -5
  105. package/dist/components/index.d.ts.map +1 -1
  106. package/dist/components/index.js +4 -4
  107. package/dist/components/index.js.map +1 -1
  108. package/dist/components/shared/base_field_layout.d.ts.map +1 -1
  109. package/dist/components/shared/base_field_layout.js +5 -1
  110. package/dist/components/shared/base_field_layout.js.map +1 -1
  111. package/dist/components/shared/field_action_array_slot.d.ts +10 -0
  112. package/dist/components/shared/field_action_array_slot.d.ts.map +1 -0
  113. package/dist/components/shared/field_action_array_slot.js +33 -0
  114. package/dist/components/shared/field_action_array_slot.js.map +1 -0
  115. package/dist/components/shared/field_action_slot.d.ts +22 -0
  116. package/dist/components/shared/field_action_slot.d.ts.map +1 -0
  117. package/dist/components/shared/field_action_slot.js +20 -0
  118. package/dist/components/shared/field_action_slot.js.map +1 -0
  119. package/dist/components/shared/ihelp_icon.d.ts +1 -1
  120. package/dist/components/shared/ihelp_icon.js +1 -1
  121. package/dist/components/shared/rule_result_card.js +1 -1
  122. package/dist/components/shared/rule_result_card.js.map +1 -1
  123. package/dist/components/shared/use_field_action_slot.d.ts +37 -0
  124. package/dist/components/shared/use_field_action_slot.d.ts.map +1 -0
  125. package/dist/components/shared/use_field_action_slot.js +77 -0
  126. package/dist/components/shared/use_field_action_slot.js.map +1 -0
  127. package/dist/components/thread_form/components/key_info_drawer.d.ts +7 -1
  128. package/dist/components/thread_form/components/key_info_drawer.d.ts.map +1 -1
  129. package/dist/components/thread_form/components/key_info_drawer.js +2 -2
  130. package/dist/components/thread_form/components/key_info_drawer.js.map +1 -1
  131. package/dist/components/thread_form/components/send_back_message.d.ts +1 -1
  132. package/dist/components/thread_form/components/send_back_message.d.ts.map +1 -1
  133. package/dist/components/thread_form/hooks/use_file_pipeline.d.ts +1 -1
  134. package/dist/components/thread_form/hooks/use_file_pipeline.d.ts.map +1 -1
  135. package/dist/components/thread_form/hooks/use_file_pipeline.js +1 -1
  136. package/dist/components/thread_form/hooks/use_file_pipeline.js.map +1 -1
  137. package/dist/components/thread_form/index.d.ts +1 -1
  138. package/dist/components/thread_form/index.d.ts.map +1 -1
  139. package/dist/components/thread_form/index.js.map +1 -1
  140. package/dist/components/thread_form/thread_form.d.ts.map +1 -1
  141. package/dist/components/thread_form/thread_form.js +3 -3
  142. package/dist/components/thread_form/thread_form.js.map +1 -1
  143. package/dist/components/thread_form/types.d.ts +32 -4
  144. package/dist/components/thread_form/types.d.ts.map +1 -1
  145. package/dist/components/thread_form/types.js.map +1 -1
  146. package/dist/lib/index.d.ts +0 -2
  147. package/dist/lib/index.d.ts.map +1 -1
  148. package/dist/lib/index.js +0 -2
  149. package/dist/lib/index.js.map +1 -1
  150. package/dist/lib/resolution_handler.d.ts +1 -1
  151. package/dist/lib/resolution_handler.d.ts.map +1 -1
  152. package/dist/lib/resolve_variable.d.ts +1 -1
  153. package/dist/lib/resolve_variable.d.ts.map +1 -1
  154. package/dist/types/clarification.d.ts +1 -1
  155. package/dist/types/clarification.d.ts.map +1 -1
  156. package/dist/types/field_action.d.ts +25 -0
  157. package/dist/types/field_action.d.ts.map +1 -0
  158. package/dist/types/field_action.js +8 -0
  159. package/dist/types/field_action.js.map +1 -0
  160. package/dist/types/index.d.ts +3 -6
  161. package/dist/types/index.d.ts.map +1 -1
  162. package/dist/types/index.js +1 -3
  163. package/dist/types/index.js.map +1 -1
  164. package/dist/types/{fb_form_data.d.ts → shared_data.d.ts} +1 -3
  165. package/dist/types/shared_data.d.ts.map +1 -0
  166. package/dist/types/{fb_form_data.js → shared_data.js} +1 -2
  167. package/dist/types/shared_data.js.map +1 -0
  168. package/dist/utils/dev_file_manager.d.ts +1 -1
  169. package/dist/utils/dev_file_manager.js +1 -1
  170. package/dist/{components/hazo_fb_form/shared → utils}/format.d.ts +2 -2
  171. package/dist/utils/format.d.ts.map +1 -0
  172. package/dist/{components/hazo_fb_form/shared → utils}/format.js +1 -1
  173. package/dist/utils/format.js.map +1 -0
  174. package/dist/utils/index.d.ts +1 -9
  175. package/dist/utils/index.d.ts.map +1 -1
  176. package/dist/utils/index.js +3 -15
  177. package/dist/utils/index.js.map +1 -1
  178. package/package.json +6 -1
  179. package/dist/components/hazo_fb_form/components/backoffice_run_button.d.ts +0 -18
  180. package/dist/components/hazo_fb_form/components/backoffice_run_button.d.ts.map +0 -1
  181. package/dist/components/hazo_fb_form/components/backoffice_run_button.js +0 -23
  182. package/dist/components/hazo_fb_form/components/backoffice_run_button.js.map +0 -1
  183. package/dist/components/hazo_fb_form/components/draft_clarification_card.d.ts +0 -39
  184. package/dist/components/hazo_fb_form/components/draft_clarification_card.d.ts.map +0 -1
  185. package/dist/components/hazo_fb_form/components/draft_clarification_card.js +0 -94
  186. package/dist/components/hazo_fb_form/components/draft_clarification_card.js.map +0 -1
  187. package/dist/components/hazo_fb_form/components/fb_document_type_editor.d.ts +0 -11
  188. package/dist/components/hazo_fb_form/components/fb_document_type_editor.d.ts.map +0 -1
  189. package/dist/components/hazo_fb_form/components/fb_document_type_editor.js +0 -82
  190. package/dist/components/hazo_fb_form/components/fb_document_type_editor.js.map +0 -1
  191. package/dist/components/hazo_fb_form/components/fb_tag_editor.d.ts +0 -11
  192. package/dist/components/hazo_fb_form/components/fb_tag_editor.d.ts.map +0 -1
  193. package/dist/components/hazo_fb_form/components/fb_tag_editor.js +0 -107
  194. package/dist/components/hazo_fb_form/components/fb_tag_editor.js.map +0 -1
  195. package/dist/components/hazo_fb_form/components/front_office_stepper.d.ts +0 -15
  196. package/dist/components/hazo_fb_form/components/front_office_stepper.d.ts.map +0 -1
  197. package/dist/components/hazo_fb_form/components/front_office_stepper.js +0 -21
  198. package/dist/components/hazo_fb_form/components/front_office_stepper.js.map +0 -1
  199. package/dist/components/hazo_fb_form/components/instance_sidebar.d.ts +0 -21
  200. package/dist/components/hazo_fb_form/components/instance_sidebar.d.ts.map +0 -1
  201. package/dist/components/hazo_fb_form/components/instance_sidebar.js +0 -58
  202. package/dist/components/hazo_fb_form/components/instance_sidebar.js.map +0 -1
  203. package/dist/components/hazo_fb_form/components/reject_clarification_dialog.d.ts +0 -15
  204. package/dist/components/hazo_fb_form/components/reject_clarification_dialog.d.ts.map +0 -1
  205. package/dist/components/hazo_fb_form/components/reject_clarification_dialog.js +0 -26
  206. package/dist/components/hazo_fb_form/components/reject_clarification_dialog.js.map +0 -1
  207. package/dist/components/hazo_fb_form/components/run_button.d.ts +0 -19
  208. package/dist/components/hazo_fb_form/components/run_button.d.ts.map +0 -1
  209. package/dist/components/hazo_fb_form/components/run_button.js +0 -38
  210. package/dist/components/hazo_fb_form/components/run_button.js.map +0 -1
  211. package/dist/components/hazo_fb_form/components/run_details_dialog.d.ts +0 -17
  212. package/dist/components/hazo_fb_form/components/run_details_dialog.d.ts.map +0 -1
  213. package/dist/components/hazo_fb_form/components/run_details_dialog.js +0 -35
  214. package/dist/components/hazo_fb_form/components/run_details_dialog.js.map +0 -1
  215. package/dist/components/hazo_fb_form/components/sent_clarification_group.d.ts +0 -30
  216. package/dist/components/hazo_fb_form/components/sent_clarification_group.d.ts.map +0 -1
  217. package/dist/components/hazo_fb_form/components/sent_clarification_group.js +0 -76
  218. package/dist/components/hazo_fb_form/components/sent_clarification_group.js.map +0 -1
  219. package/dist/components/hazo_fb_form/components/tag_pill.d.ts +0 -15
  220. package/dist/components/hazo_fb_form/components/tag_pill.d.ts.map +0 -1
  221. package/dist/components/hazo_fb_form/components/tag_pill.js +0 -15
  222. package/dist/components/hazo_fb_form/components/tag_pill.js.map +0 -1
  223. package/dist/components/hazo_fb_form/context.d.ts +0 -135
  224. package/dist/components/hazo_fb_form/context.d.ts.map +0 -1
  225. package/dist/components/hazo_fb_form/context.js +0 -13
  226. package/dist/components/hazo_fb_form/context.js.map +0 -1
  227. package/dist/components/hazo_fb_form/hazo_fb_form.d.ts +0 -13
  228. package/dist/components/hazo_fb_form/hazo_fb_form.d.ts.map +0 -1
  229. package/dist/components/hazo_fb_form/hazo_fb_form.js +0 -1188
  230. package/dist/components/hazo_fb_form/hazo_fb_form.js.map +0 -1
  231. package/dist/components/hazo_fb_form/hooks/use_fb_form_state.d.ts +0 -58
  232. package/dist/components/hazo_fb_form/hooks/use_fb_form_state.d.ts.map +0 -1
  233. package/dist/components/hazo_fb_form/hooks/use_fb_form_state.js +0 -919
  234. package/dist/components/hazo_fb_form/hooks/use_fb_form_state.js.map +0 -1
  235. package/dist/components/hazo_fb_form/hooks/use_llm_run.d.ts +0 -52
  236. package/dist/components/hazo_fb_form/hooks/use_llm_run.d.ts.map +0 -1
  237. package/dist/components/hazo_fb_form/hooks/use_llm_run.js +0 -1863
  238. package/dist/components/hazo_fb_form/hooks/use_llm_run.js.map +0 -1
  239. package/dist/components/hazo_fb_form/index.d.ts +0 -29
  240. package/dist/components/hazo_fb_form/index.d.ts.map +0 -1
  241. package/dist/components/hazo_fb_form/index.js +0 -19
  242. package/dist/components/hazo_fb_form/index.js.map +0 -1
  243. package/dist/components/hazo_fb_form/shared/agent_stepper.d.ts +0 -9
  244. package/dist/components/hazo_fb_form/shared/agent_stepper.d.ts.map +0 -1
  245. package/dist/components/hazo_fb_form/shared/agent_stepper.js +0 -17
  246. package/dist/components/hazo_fb_form/shared/agent_stepper.js.map +0 -1
  247. package/dist/components/hazo_fb_form/shared/clarification_helpers.d.ts +0 -15
  248. package/dist/components/hazo_fb_form/shared/clarification_helpers.d.ts.map +0 -1
  249. package/dist/components/hazo_fb_form/shared/clarification_helpers.js +0 -23
  250. package/dist/components/hazo_fb_form/shared/clarification_helpers.js.map +0 -1
  251. package/dist/components/hazo_fb_form/shared/file_status_accordion.d.ts +0 -9
  252. package/dist/components/hazo_fb_form/shared/file_status_accordion.d.ts.map +0 -1
  253. package/dist/components/hazo_fb_form/shared/file_status_accordion.js +0 -39
  254. package/dist/components/hazo_fb_form/shared/file_status_accordion.js.map +0 -1
  255. package/dist/components/hazo_fb_form/shared/file_utils.d.ts +0 -9
  256. package/dist/components/hazo_fb_form/shared/file_utils.d.ts.map +0 -1
  257. package/dist/components/hazo_fb_form/shared/file_utils.js +0 -31
  258. package/dist/components/hazo_fb_form/shared/file_utils.js.map +0 -1
  259. package/dist/components/hazo_fb_form/shared/format.d.ts.map +0 -1
  260. package/dist/components/hazo_fb_form/shared/format.js.map +0 -1
  261. package/dist/components/hazo_fb_form/shared/group_debug_icon.d.ts +0 -15
  262. package/dist/components/hazo_fb_form/shared/group_debug_icon.d.ts.map +0 -1
  263. package/dist/components/hazo_fb_form/shared/group_debug_icon.js +0 -48
  264. package/dist/components/hazo_fb_form/shared/group_debug_icon.js.map +0 -1
  265. package/dist/components/hazo_fb_form/shared/index.d.ts +0 -10
  266. package/dist/components/hazo_fb_form/shared/index.d.ts.map +0 -1
  267. package/dist/components/hazo_fb_form/shared/index.js +0 -9
  268. package/dist/components/hazo_fb_form/shared/index.js.map +0 -1
  269. package/dist/components/hazo_fb_form/shared/pdf_side_panel.d.ts +0 -22
  270. package/dist/components/hazo_fb_form/shared/pdf_side_panel.d.ts.map +0 -1
  271. package/dist/components/hazo_fb_form/shared/pdf_side_panel.js +0 -10
  272. package/dist/components/hazo_fb_form/shared/pdf_side_panel.js.map +0 -1
  273. package/dist/components/hazo_fb_form/shared/send_back_item_card.d.ts +0 -44
  274. package/dist/components/hazo_fb_form/shared/send_back_item_card.d.ts.map +0 -1
  275. package/dist/components/hazo_fb_form/shared/send_back_item_card.js +0 -80
  276. package/dist/components/hazo_fb_form/shared/send_back_item_card.js.map +0 -1
  277. package/dist/components/hazo_fb_form/shared/use_pdf_viewer.d.ts +0 -28
  278. package/dist/components/hazo_fb_form/shared/use_pdf_viewer.d.ts.map +0 -1
  279. package/dist/components/hazo_fb_form/shared/use_pdf_viewer.js +0 -46
  280. package/dist/components/hazo_fb_form/shared/use_pdf_viewer.js.map +0 -1
  281. package/dist/components/hazo_fb_form/types.d.ts +0 -372
  282. package/dist/components/hazo_fb_form/types.d.ts.map +0 -1
  283. package/dist/components/hazo_fb_form/types.js +0 -5
  284. package/dist/components/hazo_fb_form/types.js.map +0 -1
  285. package/dist/components/hazo_fb_form/views/back_office_view.d.ts +0 -7
  286. package/dist/components/hazo_fb_form/views/back_office_view.d.ts.map +0 -1
  287. package/dist/components/hazo_fb_form/views/back_office_view.js +0 -425
  288. package/dist/components/hazo_fb_form/views/back_office_view.js.map +0 -1
  289. package/dist/components/hazo_fb_form/views/clarifications_view.d.ts +0 -16
  290. package/dist/components/hazo_fb_form/views/clarifications_view.d.ts.map +0 -1
  291. package/dist/components/hazo_fb_form/views/clarifications_view.js +0 -291
  292. package/dist/components/hazo_fb_form/views/clarifications_view.js.map +0 -1
  293. package/dist/components/hazo_fb_form/views/client_data_view.d.ts +0 -6
  294. package/dist/components/hazo_fb_form/views/client_data_view.d.ts.map +0 -1
  295. package/dist/components/hazo_fb_form/views/client_data_view.js +0 -39
  296. package/dist/components/hazo_fb_form/views/client_data_view.js.map +0 -1
  297. package/dist/components/hazo_fb_form/views/front_office_view.d.ts +0 -6
  298. package/dist/components/hazo_fb_form/views/front_office_view.d.ts.map +0 -1
  299. package/dist/components/hazo_fb_form/views/front_office_view.js +0 -1351
  300. package/dist/components/hazo_fb_form/views/front_office_view.js.map +0 -1
  301. package/dist/components/hazo_fb_form/views/interim_view.d.ts +0 -8
  302. package/dist/components/hazo_fb_form/views/interim_view.d.ts.map +0 -1
  303. package/dist/components/hazo_fb_form/views/interim_view.js +0 -535
  304. package/dist/components/hazo_fb_form/views/interim_view.js.map +0 -1
  305. package/dist/components/hazo_fb_form/views/review_queue_view.d.ts +0 -14
  306. package/dist/components/hazo_fb_form/views/review_queue_view.d.ts.map +0 -1
  307. package/dist/components/hazo_fb_form/views/review_queue_view.js +0 -230
  308. package/dist/components/hazo_fb_form/views/review_queue_view.js.map +0 -1
  309. package/dist/components/hazo_fb_form/views/send_back_view.d.ts +0 -13
  310. package/dist/components/hazo_fb_form/views/send_back_view.d.ts.map +0 -1
  311. package/dist/components/hazo_fb_form/views/send_back_view.js +0 -258
  312. package/dist/components/hazo_fb_form/views/send_back_view.js.map +0 -1
  313. package/dist/components/hazo_fb_form/views/summary_review_view.d.ts +0 -17
  314. package/dist/components/hazo_fb_form/views/summary_review_view.d.ts.map +0 -1
  315. package/dist/components/hazo_fb_form/views/summary_review_view.js +0 -258
  316. package/dist/components/hazo_fb_form/views/summary_review_view.js.map +0 -1
  317. package/dist/components/shared/json_data_panel/index.d.ts +0 -3
  318. package/dist/components/shared/json_data_panel/index.d.ts.map +0 -1
  319. package/dist/components/shared/json_data_panel/index.js +0 -2
  320. package/dist/components/shared/json_data_panel/index.js.map +0 -1
  321. package/dist/components/shared/json_data_panel/json_data_panel.d.ts +0 -28
  322. package/dist/components/shared/json_data_panel/json_data_panel.d.ts.map +0 -1
  323. package/dist/components/shared/json_data_panel/json_data_panel.js +0 -156
  324. package/dist/components/shared/json_data_panel/json_data_panel.js.map +0 -1
  325. package/dist/lib/fb_form_handler.d.ts +0 -63
  326. package/dist/lib/fb_form_handler.d.ts.map +0 -1
  327. package/dist/lib/fb_form_handler.js +0 -425
  328. package/dist/lib/fb_form_handler.js.map +0 -1
  329. package/dist/types/fb_form_data.d.ts.map +0 -1
  330. package/dist/types/fb_form_data.js.map +0 -1
  331. package/dist/types/fb_form_data_v1.d.ts +0 -250
  332. package/dist/types/fb_form_data_v1.d.ts.map +0 -1
  333. package/dist/types/fb_form_data_v1.js +0 -117
  334. package/dist/types/fb_form_data_v1.js.map +0 -1
  335. package/dist/types/fb_form_instance.d.ts +0 -49
  336. package/dist/types/fb_form_instance.d.ts.map +0 -1
  337. package/dist/types/fb_form_instance.js +0 -10
  338. package/dist/types/fb_form_instance.js.map +0 -1
  339. package/dist/utils/fb_data_adapter.d.ts +0 -33
  340. package/dist/utils/fb_data_adapter.d.ts.map +0 -1
  341. package/dist/utils/fb_data_adapter.js +0 -436
  342. package/dist/utils/fb_data_adapter.js.map +0 -1
  343. package/dist/utils/fb_data_adapter_v2.d.ts +0 -17
  344. package/dist/utils/fb_data_adapter_v2.d.ts.map +0 -1
  345. package/dist/utils/fb_data_adapter_v2.js +0 -483
  346. package/dist/utils/fb_data_adapter_v2.js.map +0 -1
  347. package/dist/utils/fb_data_helpers.d.ts +0 -86
  348. package/dist/utils/fb_data_helpers.d.ts.map +0 -1
  349. package/dist/utils/fb_data_helpers.js +0 -269
  350. package/dist/utils/fb_data_helpers.js.map +0 -1
  351. package/dist/utils/fb_data_mutations.d.ts +0 -43
  352. package/dist/utils/fb_data_mutations.d.ts.map +0 -1
  353. package/dist/utils/fb_data_mutations.js +0 -379
  354. package/dist/utils/fb_data_mutations.js.map +0 -1
  355. package/dist/utils/fb_data_mutations_v2.d.ts +0 -46
  356. package/dist/utils/fb_data_mutations_v2.d.ts.map +0 -1
  357. package/dist/utils/fb_data_mutations_v2.js +0 -341
  358. package/dist/utils/fb_data_mutations_v2.js.map +0 -1
  359. package/dist/utils/fb_data_queries.d.ts +0 -81
  360. package/dist/utils/fb_data_queries.d.ts.map +0 -1
  361. package/dist/utils/fb_data_queries.js +0 -354
  362. package/dist/utils/fb_data_queries.js.map +0 -1
@@ -1,1351 +0,0 @@
1
- /**
2
- * Front Office View - Client-facing questions with file_textbox fields.
3
- * Supports + icons for adding fields/groups when callbacks are provided.
4
- */
5
- 'use client';
6
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
7
- import React, { useCallback, useMemo, useState, useEffect, useRef } from 'react';
8
- import { useFbFormContext } from '../context.js';
9
- import { normalize_file_textbox_value } from '../../../types/file_textbox.js';
10
- import { HazoCollabFormFileTextbox } from '../../hazo_collab_form_file_textbox/index.js';
11
- import { PdfSidePanel } from '../shared/pdf_side_panel.js';
12
- import { CollabFormAddFieldIcon } from '../../hazo_collab_form_base.js';
13
- import { ClarificationSection } from '../../clarification/clarification_section.js';
14
- import { optional_import } from '../../../utils/optional_import.js';
15
- import { SummaryReviewView } from './summary_review_view.js';
16
- import { HazoUiDialog } from 'hazo_ui';
17
- import { HiCheckCircle, HiExclamationCircle, HiChevronRight } from 'react-icons/hi';
18
- import { LuLoader } from 'react-icons/lu';
19
- import { rule_to_fb_execution } from '../../../utils/rule_to_execution.js';
20
- import { FrontOfficeStepper } from '../components/front_office_stepper.js';
21
- import { is_fb_terminal_status } from '../../../types/fb_form_instance.js';
22
- import { IhelpIcon } from '../../shared/ihelp_icon.js';
23
- import { DescriptionWithIhelp } from '../../shared/description_with_ihelp.js';
24
- import { infer_mime_type } from '../shared/format.js';
25
- // Lazy-load HazoAddFieldDialog to avoid pulling dnd-kit into the initial chunk
26
- const LazyAddFieldDialog = React.lazy(() => import('../../hazo_add_field_dialog/hazo_add_field_dialog.js').then(mod => ({ default: mod.HazoAddFieldDialog })));
27
- const LazyAddGroupDialog = React.lazy(() => import('../../hazo_add_group_dialog/hazo_add_group_dialog.js').then(mod => ({ default: mod.HazoAddGroupDialog })));
28
- export function FrontOfficeView() {
29
- const { props, classification_results, trigger_classify_file, classifying_file_ids, queued_file_ids, autofilling_file_ids, sent_clarifications, active_instance, is_multi_instance, all_clarifications, pending_clarification_responses, set_pending_clarification_responses, file_validation_results, set_file_validation_results, validating_file_ids, set_validating_file_ids, draft_clarifications, update_clarification_response, form_data_entries, } = useFbFormContext();
30
- const { front_sections, front_form_data, on_front_change, field_textbox_configs, file_manager } = props;
31
- const { on_add_front_field, on_add_front_group } = props;
32
- const show_tags = props.show_classification_tags !== false; // default true
33
- const can_add_fields = !!on_add_front_field;
34
- const can_add_groups = !!on_add_front_group;
35
- const [viewed_file, set_viewed_file] = useState(null);
36
- const [PdfViewerComponent, set_pdf_viewer] = useState(null);
37
- // Add field dialog state
38
- const [add_field_open, set_add_field_open] = useState(false);
39
- const [add_field_after_id, set_add_field_after_id] = useState('');
40
- // Add group dialog state
41
- const [add_group_open, set_add_group_open] = useState(false);
42
- const [add_group_after_id, set_add_group_after_id] = useState('');
43
- // Validation detail dialog state
44
- const [validation_detail_file_id, set_validation_detail_file_id] = useState(null);
45
- // Stepper state — active_front_step lives in context so it persists across tab switches
46
- const use_stepper = props.front_office_stepper !== false;
47
- const is_past_instance = active_instance ? is_fb_terminal_status(active_instance.status) : false;
48
- const is_submitted = active_instance?.status === 'client_submitted' || active_instance?.status === 'client_submitted_auto_review';
49
- const { active_front_step, set_active_front_step } = useFbFormContext();
50
- const [completed_steps, set_completed_steps] = useState(new Set());
51
- const [front_submitted, set_front_submitted] = useState(false);
52
- const is_front_read_only = front_submitted || is_past_instance || is_submitted;
53
- // Response validation state keyed by clarification_id
54
- const [response_validations, set_response_validations] = useState({});
55
- // Pipeline step tracking per file_id (for 3-step response validation flow)
56
- const [file_pipeline_steps, set_file_pipeline_steps] = useState({});
57
- // Cached prompt template for response validation
58
- const response_validation_prompt_ref = useRef(null);
59
- const prompt_fetch_promise_ref = useRef(null);
60
- // Guard: in-flight validation per clarification_id (prevents concurrent calls)
61
- const validating_ids_ref = useRef(new Set());
62
- // Ref to current front_form_data to avoid stale closures in async callbacks
63
- const front_form_data_ref = useRef(front_form_data);
64
- front_form_data_ref.current = front_form_data;
65
- // Use externally provided PdfViewer if available, otherwise lazy-load from hazo_pdf
66
- useEffect(() => {
67
- if (props.pdf_viewer) {
68
- set_pdf_viewer(() => props.pdf_viewer);
69
- return;
70
- }
71
- let cancelled = false;
72
- optional_import('hazo_pdf')
73
- .then((mod) => {
74
- if (!cancelled && mod.PdfViewer)
75
- set_pdf_viewer(() => mod.PdfViewer);
76
- })
77
- .catch(() => { });
78
- return () => { cancelled = true; };
79
- }, [props.pdf_viewer]);
80
- // ── Immediate validation on file upload ──
81
- const has_validation_pipeline = !!props.validation_api_endpoint && (props.validation_rules?.length ?? 0) > 0;
82
- const has_classification_pipeline = !!props.llm_api_endpoint && !!field_textbox_configs;
83
- // Trigger immediate validation for a single file
84
- const trigger_validate_file = useCallback(async (field_id, attachment) => {
85
- if (!props.validation_api_endpoint || !props.validation_rules?.length) {
86
- return;
87
- }
88
- // Check for unsupported file types (images, video, audio)
89
- const mime = attachment.mime_type?.toLowerCase() ?? '';
90
- if (mime.startsWith('image/') || mime.startsWith('video/') || mime.startsWith('audio/')) {
91
- const type_label = mime.startsWith('image/') ? 'image' : mime.startsWith('video/') ? 'video' : 'audio';
92
- set_file_validation_results(prev => ({
93
- ...prev,
94
- [attachment.file_id]: {
95
- file_id: attachment.file_id,
96
- file_name: attachment.file_name,
97
- status: 'failed',
98
- errors: [],
99
- document_types: [],
100
- failure_reason: `This file type (${mime}) cannot be validated. Only PDF and text-based documents are supported for automated validation.`,
101
- },
102
- }));
103
- // Update pipeline step if in a pipeline
104
- set_file_pipeline_steps(prev => {
105
- const steps = prev[attachment.file_id];
106
- if (!steps)
107
- return prev;
108
- return {
109
- ...prev,
110
- [attachment.file_id]: steps.map(s => s.id === 'doc_type_validation'
111
- ? { ...s, status: 'failed', error: `Unsupported ${type_label} file type for validation` }
112
- : s),
113
- };
114
- });
115
- return;
116
- }
117
- // Only run rules with check_type 'immediate' or unset (backward compat)
118
- const rules = props.validation_rules.filter(r => r.enabled && (!r.check_type || r.check_type === 'immediate'));
119
- if (rules.length === 0) {
120
- return;
121
- }
122
- // Mark as validating
123
- set_validating_file_ids(prev => { const s = new Set(prev); s.add(attachment.file_id); return s; });
124
- try {
125
- const download_url = file_manager?.callbacks?.get_download_url?.(attachment.file_id, 'public') ?? '';
126
- const rules_to_execute = rules.map(rule_to_fb_execution);
127
- const response = await fetch(props.validation_api_endpoint, {
128
- method: 'POST',
129
- headers: { 'Content-Type': 'application/json' },
130
- body: JSON.stringify({
131
- file_name: attachment.file_name,
132
- mime_type: attachment.mime_type,
133
- download_url,
134
- rules: rules_to_execute,
135
- }),
136
- });
137
- const result = await response.json();
138
- // Enrich clarification doc_references with actual file_id
139
- const enriched_clarifications = (result.clarifications ?? []).map(c => ({
140
- ...c,
141
- doc_references: c.doc_references.map(ref => ({
142
- ...ref,
143
- file_id: ref.file_id || attachment.file_id,
144
- })),
145
- }));
146
- // Enrich rule_results with human-readable rule names
147
- const rule_name_lookup = new Map(rules.map(r => [r.rule_id, r.name]));
148
- const enriched_rule_results = result.rule_results?.map(rr => ({
149
- ...rr,
150
- rule_name: rr.rule_name ?? rule_name_lookup.get(rr.rule_id),
151
- }));
152
- // Store as FbFileValidationResult → auto-populates draft_clarifications via useEffect in use_fb_form_state
153
- // Merge with existing rule_results AND errors (e.g., response validation) rather than replacing
154
- set_file_validation_results(prev => {
155
- const existing = prev[attachment.file_id];
156
- const existing_rule_results = existing?.rule_results ?? [];
157
- // Keep prior rule_results that aren't being re-run (e.g., response_validation)
158
- const new_rule_ids = new Set(enriched_rule_results?.map(r => r.rule_id) ?? []);
159
- const kept_prior = existing_rule_results.filter(r => !new_rule_ids.has(r.rule_id));
160
- // Keep prior errors from earlier pipeline steps (e.g., response validation clarifications)
161
- const new_error_ids = new Set(enriched_clarifications.map((c) => c.id));
162
- const kept_errors = (existing?.errors ?? []).filter((c) => !new_error_ids.has(c.id));
163
- const merged_errors = [...kept_errors, ...enriched_clarifications];
164
- return {
165
- ...prev,
166
- [attachment.file_id]: {
167
- file_id: attachment.file_id,
168
- file_name: attachment.file_name,
169
- status: merged_errors.length > 0 ? 'failed' : (kept_prior.some(r => r.issues.length > 0) ? 'failed' : 'passed'),
170
- errors: merged_errors,
171
- document_types: existing?.document_types ?? [],
172
- rule_results: [...kept_prior, ...(enriched_rule_results ?? [])],
173
- },
174
- };
175
- });
176
- }
177
- catch (err) {
178
- console.error('[FrontOfficeView] Immediate validation failed:', err);
179
- set_file_validation_results(prev => ({
180
- ...prev,
181
- [attachment.file_id]: {
182
- file_id: attachment.file_id,
183
- file_name: attachment.file_name,
184
- status: 'failed',
185
- errors: [],
186
- document_types: [],
187
- },
188
- }));
189
- }
190
- finally {
191
- set_validating_file_ids(prev => { const s = new Set(prev); s.delete(attachment.file_id); return s; });
192
- }
193
- }, [props.validation_api_endpoint, props.validation_rules, file_manager, set_validating_file_ids, set_file_validation_results]);
194
- // Track known file IDs to auto-trigger immediate validation on newly uploaded files
195
- const known_file_ids_ref = useRef(new Set());
196
- // Seed known file IDs from initial form data (avoids validating pre-existing files)
197
- useEffect(() => {
198
- const ids = new Set();
199
- for (const [, value] of Object.entries(front_form_data)) {
200
- const blocks = normalize_file_textbox_value(value);
201
- for (const b of blocks) {
202
- if (b.type === 'file' && b.attachment?.file_id)
203
- ids.add(b.attachment.file_id);
204
- }
205
- }
206
- known_file_ids_ref.current = ids;
207
- }, []); // Only on mount
208
- // Detect new file blocks and auto-trigger classification + validation
209
- useEffect(() => {
210
- if (!has_validation_pipeline && !has_classification_pipeline)
211
- return;
212
- for (const section of front_sections) {
213
- for (const group of section.groups ?? []) {
214
- for (const field of group.fields ?? []) {
215
- const blocks = normalize_file_textbox_value(front_form_data[field.id]);
216
- for (const b of blocks) {
217
- if (b.type === 'file' && b.attachment?.file_id && !known_file_ids_ref.current.has(b.attachment.file_id)) {
218
- known_file_ids_ref.current.add(b.attachment.file_id);
219
- if (has_classification_pipeline) {
220
- // Full pipeline: classify → validate → route to back office
221
- trigger_classify_file(field.id, b.attachment.file_id, b.attachment.file_name);
222
- }
223
- else if (has_validation_pipeline) {
224
- // Validation only (no classification configured)
225
- trigger_validate_file(field.id, b.attachment);
226
- }
227
- }
228
- }
229
- }
230
- }
231
- }
232
- }, [front_form_data, front_sections, has_validation_pipeline, has_classification_pipeline, trigger_validate_file, trigger_classify_file]);
233
- const handle_field_change = useCallback((field_id) => (value) => {
234
- on_front_change?.(field_id, value);
235
- }, [on_front_change]);
236
- const handle_file_view = useCallback((attachment) => {
237
- set_viewed_file((prev) => prev?.file_id === attachment.file_id ? null : attachment);
238
- }, []);
239
- const handle_classify_file = useCallback((field_id, attachment) => {
240
- trigger_classify_file(field_id, attachment.file_id, attachment.file_name);
241
- }, [trigger_classify_file]);
242
- // ── Response validation helpers ──
243
- /** Fetch and cache the clarification response validation prompt template */
244
- const get_response_validation_prompt = useCallback(async () => {
245
- if (response_validation_prompt_ref.current)
246
- return response_validation_prompt_ref.current;
247
- if (prompt_fetch_promise_ref.current)
248
- return prompt_fetch_promise_ref.current;
249
- const prompts_endpoint = props.prompts_api_endpoint;
250
- if (!prompts_endpoint)
251
- return null;
252
- prompt_fetch_promise_ref.current = (async () => {
253
- try {
254
- const res = await fetch(`${prompts_endpoint}/clarification/validate_response`);
255
- if (!res.ok)
256
- return null;
257
- const data = await res.json();
258
- const text = data?.data?.prompt_text_full ?? data?.prompt_text_full ?? null;
259
- if (text)
260
- response_validation_prompt_ref.current = text;
261
- return text;
262
- }
263
- catch (err) {
264
- console.error('[FrontOfficeView] Failed to fetch response validation prompt:', err);
265
- return null;
266
- }
267
- })();
268
- return prompt_fetch_promise_ref.current;
269
- }, [props.prompts_api_endpoint]);
270
- /** Validate clarification response files against the prompt template.
271
- * When multiple files: sends batch mode so LLM evaluates them collectively.
272
- * When single file: sends single mode (backward compatible). */
273
- const validate_response_batch = useCallback(async (item, response) => {
274
- const validation_endpoint = props.validation_api_endpoint;
275
- if (!validation_endpoint || !response.response_files?.length)
276
- return { passed: true };
277
- try {
278
- const template = await get_response_validation_prompt();
279
- if (!template)
280
- return { passed: true }; // No prompt configured, skip
281
- const variables = {
282
- issue_description: item.issue_description || '',
283
- validation_details: item.validation_details || '',
284
- response_choice: response.response_choice || '',
285
- };
286
- let prompt = template;
287
- for (const [key, value] of Object.entries(variables)) {
288
- prompt = prompt.replace(new RegExp(`\\{\\{${key}\\}\\}`, 'g'), value);
289
- }
290
- const attachments = response.response_files;
291
- const is_batch = attachments.length > 1;
292
- // Build request body — infer MIME type from file name if the attachment has a generic type
293
- const files = attachments.map(att => ({
294
- file_id: att.file_id,
295
- file_name: att.file_name,
296
- mime_type: (!att.mime_type || att.mime_type === 'application/octet-stream')
297
- ? infer_mime_type(att.file_name)
298
- : att.mime_type,
299
- download_url: file_manager?.callbacks?.get_download_url?.(att.file_id, 'public') ?? '',
300
- }));
301
- const body = {
302
- file_name: files[0].file_name,
303
- mime_type: files[0].mime_type,
304
- rules: [{
305
- rule_id: 'response_validation',
306
- name: 'Response Validation',
307
- prompt,
308
- target_field_id: item.target_field_id,
309
- target_label: item.target_label,
310
- clarification_type: 'invalid_document',
311
- }],
312
- variables,
313
- };
314
- if (is_batch) {
315
- body.mode = 'batch';
316
- body.files = files;
317
- }
318
- else {
319
- body.download_url = files[0].download_url;
320
- }
321
- const res = await fetch(validation_endpoint, {
322
- method: 'POST',
323
- headers: { 'Content-Type': 'application/json' },
324
- body: JSON.stringify(body),
325
- });
326
- const result = await res.json();
327
- const rule_result = result.rule_results?.[0];
328
- const passed = !rule_result || rule_result.issues.length === 0;
329
- // Enrich clarification doc_references with actual file_ids
330
- // Match by file_name first, then fallback. If no doc_references, assign all response files.
331
- const clarifications = (result.clarifications ?? []).map((c) => ({
332
- ...c,
333
- doc_references: c.doc_references.length > 0
334
- ? c.doc_references.map((ref) => ({
335
- ...ref,
336
- file_id: ref.file_id
337
- || attachments.find(a => a.file_name === ref.file_name)?.file_id
338
- || attachments[0]?.file_id
339
- || '',
340
- }))
341
- : attachments.map(att => ({
342
- file_id: att.file_id,
343
- file_name: att.file_name,
344
- mime_type: att.mime_type,
345
- })),
346
- }));
347
- // Prefer summary (rule-level explanation), fall back to the first issue's description or raw_response
348
- let details = rule_result?.summary ?? rule_result?.issues[0]?.issue_description;
349
- if (!details && rule_result?.raw_response) {
350
- try {
351
- const raw = typeof rule_result.raw_response === 'string' ? JSON.parse(rule_result.raw_response) : rule_result.raw_response;
352
- details = raw?.summary;
353
- }
354
- catch { /* ignore parse errors */ }
355
- }
356
- return { passed, details, rule_result, clarifications };
357
- }
358
- catch (err) {
359
- console.error('[FrontOfficeView] Response batch validation error:', err);
360
- return { passed: false, details: err instanceof Error ? err.message : 'Validation request failed' };
361
- }
362
- }, [props.validation_api_endpoint, get_response_validation_prompt, file_manager]);
363
- /** Helper to update a single pipeline step for a file */
364
- const update_pipeline_step = useCallback((file_id, step_id, update) => {
365
- set_file_pipeline_steps(prev => {
366
- const steps = prev[file_id];
367
- if (!steps)
368
- return prev;
369
- return {
370
- ...prev,
371
- [file_id]: steps.map(s => s.id === step_id ? { ...s, ...update } : s),
372
- };
373
- });
374
- }, []);
375
- /** Validate all files in a clarification response.
376
- * 3-step pipeline: Response Validation → Classification → Doc Type Validation.
377
- * Stores per-file results in file_validation_results (for file bar badges + dialog). */
378
- const validate_clarification_response = useCallback(async (clarification_id, item, response) => {
379
- if (!response.response_files?.length)
380
- return true;
381
- // Guard against concurrent validation for the same clarification
382
- if (validating_ids_ref.current.has(clarification_id))
383
- return false;
384
- validating_ids_ref.current.add(clarification_id);
385
- set_response_validations(prev => ({
386
- ...prev,
387
- [clarification_id]: { status: 'validating' },
388
- }));
389
- // Initialize 3-step pipeline for each response file
390
- for (const att of response.response_files) {
391
- set_validating_file_ids(prev => { const s = new Set(prev); s.add(att.file_id); return s; });
392
- set_file_validation_results(prev => ({
393
- ...prev,
394
- [att.file_id]: {
395
- file_id: att.file_id,
396
- file_name: att.file_name,
397
- status: 'validating',
398
- errors: [],
399
- document_types: [],
400
- rule_results: [],
401
- },
402
- }));
403
- set_file_pipeline_steps(prev => ({
404
- ...prev,
405
- [att.file_id]: [
406
- { id: 'response_validation', label: 'Response Validation', status: 'running' },
407
- { id: 'classification', label: 'Document Classification', status: 'pending' },
408
- { id: 'doc_type_validation', label: 'Document Type Validation', status: 'pending' },
409
- ],
410
- }));
411
- }
412
- try {
413
- const all_details = [];
414
- // ── Step 1: Response Validation (batch — all files evaluated collectively) ──
415
- const batch_result = await validate_response_batch(item, response);
416
- // Apply batch result to ALL files uniformly
417
- for (const att of response.response_files) {
418
- update_pipeline_step(att.file_id, 'response_validation', {
419
- status: batch_result.passed ? 'passed' : 'failed',
420
- details: batch_result.details,
421
- error: batch_result.passed ? undefined : batch_result.details,
422
- });
423
- // Store per-file result — all files share the batch clarification result (group validation)
424
- set_file_validation_results(prev => ({
425
- ...prev,
426
- [att.file_id]: {
427
- file_id: att.file_id,
428
- file_name: att.file_name,
429
- status: batch_result.passed ? 'passed' : 'failed',
430
- errors: batch_result.clarifications ?? [],
431
- document_types: [],
432
- rule_results: batch_result.rule_result ? [{
433
- ...batch_result.rule_result,
434
- rule_name: 'Response Validation',
435
- }] : [],
436
- },
437
- }));
438
- if (!batch_result.passed) {
439
- update_pipeline_step(att.file_id, 'classification', { status: 'skipped' });
440
- update_pipeline_step(att.file_id, 'doc_type_validation', { status: 'skipped' });
441
- set_validating_file_ids(prev => { const s = new Set(prev); s.delete(att.file_id); return s; });
442
- }
443
- }
444
- if (batch_result.details) {
445
- all_details.push(batch_result.details);
446
- }
447
- if (!batch_result.passed) {
448
- set_response_validations(prev => ({
449
- ...prev,
450
- [clarification_id]: {
451
- status: 'failed',
452
- error: batch_result.details || 'Response validation failed',
453
- },
454
- }));
455
- return false;
456
- }
457
- // ── Step 2: Classification → Step 3: Validation ──
458
- // trigger_classify_file needs a field_textbox_config. Resolve the source field_id
459
- // from the clarification's doc_references (the original field the file belongs to).
460
- const source_field_id = item.doc_references?.[0]?.source_field_id;
461
- const classify_field_id = source_field_id && field_textbox_configs?.[source_field_id]
462
- ? source_field_id
463
- : Object.keys(field_textbox_configs ?? {})[0]; // fallback to first field
464
- const can_classify = has_classification_pipeline && !!classify_field_id && !!field_textbox_configs?.[classify_field_id];
465
- for (const att of response.response_files) {
466
- if (can_classify) {
467
- // Classification → validation (trigger_classify_file chains to validation internally)
468
- update_pipeline_step(att.file_id, 'classification', { status: 'running' });
469
- trigger_classify_file(classify_field_id, att.file_id, att.file_name);
470
- }
471
- else if (has_validation_pipeline) {
472
- // No classification config — skip to validation
473
- update_pipeline_step(att.file_id, 'classification', { status: 'skipped' });
474
- update_pipeline_step(att.file_id, 'doc_type_validation', { status: 'running' });
475
- trigger_validate_file(item.target_field_id, att);
476
- }
477
- else {
478
- update_pipeline_step(att.file_id, 'classification', { status: 'skipped' });
479
- update_pipeline_step(att.file_id, 'doc_type_validation', { status: 'skipped' });
480
- set_validating_file_ids(prev => { const s = new Set(prev); s.delete(att.file_id); return s; });
481
- }
482
- }
483
- set_response_validations(prev => ({
484
- ...prev,
485
- [clarification_id]: {
486
- status: 'passed',
487
- message: all_details.length > 0 ? all_details.join('; ') : 'Response files validated',
488
- },
489
- }));
490
- return true;
491
- }
492
- catch (err) {
493
- // Clear validating state for all files on error
494
- for (const att of (response.response_files ?? [])) {
495
- set_validating_file_ids(prev => { const s = new Set(prev); s.delete(att.file_id); return s; });
496
- // Mark all pending steps as failed
497
- update_pipeline_step(att.file_id, 'response_validation', { status: 'failed', error: 'Pipeline error' });
498
- update_pipeline_step(att.file_id, 'classification', { status: 'failed', error: 'Pipeline error' });
499
- update_pipeline_step(att.file_id, 'doc_type_validation', { status: 'failed', error: 'Pipeline error' });
500
- }
501
- set_response_validations(prev => ({
502
- ...prev,
503
- [clarification_id]: {
504
- status: 'failed',
505
- error: err instanceof Error ? err.message : 'Validation failed',
506
- },
507
- }));
508
- return false;
509
- }
510
- finally {
511
- validating_ids_ref.current.delete(clarification_id);
512
- }
513
- }, [validate_response_batch, set_file_validation_results, set_validating_file_ids, has_validation_pipeline, has_classification_pipeline, trigger_validate_file, trigger_classify_file, update_pipeline_step, field_textbox_configs]);
514
- // ── Reactive pipeline step updates based on classifying/validating state changes ──
515
- useEffect(() => {
516
- set_file_pipeline_steps(prev => {
517
- let changed = false;
518
- const next = { ...prev };
519
- for (const [file_id, steps] of Object.entries(next)) {
520
- const cls_step = steps.find(s => s.id === 'classification');
521
- const val_step = steps.find(s => s.id === 'doc_type_validation');
522
- // Classification finished → mark passed, validation will start via trigger_classify_file's internal chaining
523
- if (cls_step?.status === 'running' && !classifying_file_ids.has(file_id)) {
524
- changed = true;
525
- next[file_id] = steps.map(s => {
526
- if (s.id === 'classification')
527
- return { ...s, status: 'passed', details: 'Document classified' };
528
- if (s.id === 'doc_type_validation' && s.status === 'pending')
529
- return { ...s, status: 'running' };
530
- return s;
531
- });
532
- }
533
- if (val_step?.status === 'running' && !validating_file_ids.has(file_id)) {
534
- // Validation finished — check result
535
- const vr = file_validation_results[file_id];
536
- changed = true;
537
- next[file_id] = steps.map(s => {
538
- if (s.id === 'doc_type_validation') {
539
- const passed = vr?.status === 'passed' || vr?.status === 'skipped';
540
- const checks_run = vr?.rule_results?.length ?? 0;
541
- const checks_failed = vr?.rule_results?.filter(r => r.issues.length > 0).length ?? 0;
542
- return {
543
- ...s,
544
- status: (passed ? 'passed' : 'failed'),
545
- details: passed
546
- ? `${checks_run} check${checks_run !== 1 ? 's' : ''} passed`
547
- : `${checks_failed} of ${checks_run} check${checks_run !== 1 ? 's' : ''} failed`,
548
- error: !passed ? (vr?.failure_reason || undefined) : undefined,
549
- };
550
- }
551
- return s;
552
- });
553
- }
554
- }
555
- return changed ? next : prev;
556
- });
557
- }, [classifying_file_ids, validating_file_ids, file_validation_results]);
558
- const handle_open_add_field = useCallback((after_field_id) => {
559
- set_add_field_after_id(after_field_id);
560
- set_add_field_open(true);
561
- }, []);
562
- const handle_confirm_add_fields = useCallback((fields) => {
563
- if (!on_add_front_field)
564
- return;
565
- for (const field of fields) {
566
- on_add_front_field(add_field_after_id, field);
567
- }
568
- }, [on_add_front_field, add_field_after_id]);
569
- const handle_open_add_group = useCallback((after_group_id) => {
570
- set_add_group_after_id(after_group_id);
571
- set_add_group_open(true);
572
- }, []);
573
- const handle_confirm_add_group = useCallback((group) => {
574
- if (!on_add_front_group)
575
- return;
576
- on_add_front_group(add_group_after_id, group);
577
- }, [on_add_front_group, add_group_after_id]);
578
- // Build per-file tag maps from per-file classifications
579
- const per_field_file_tags = useMemo(() => {
580
- if (!show_tags)
581
- return {};
582
- const result = {};
583
- for (const classification of classification_results) {
584
- const config = field_textbox_configs?.[classification.field_id];
585
- if (!classification.file_classifications)
586
- continue;
587
- const available_tags = config?.classification.available_tags ?? props.available_tags ?? [];
588
- const file_map = {};
589
- for (const fc of classification.file_classifications) {
590
- const matched = available_tags
591
- .filter((t) => fc.tags.includes(t.tag_id))
592
- .map((t) => ({ id: t.tag_id, label: t.tag_label, color: t.color }));
593
- if (matched.length > 0) {
594
- file_map[fc.file_id] = matched;
595
- }
596
- else {
597
- // File was classified but matched no tags — show "Unknown" with reason tooltip
598
- file_map[fc.file_id] = [{
599
- id: '__unknown',
600
- label: 'Unknown',
601
- color: 'bg-gray-100 text-gray-600',
602
- tooltip: fc.unclassified_reason || 'Could not match to any classification category',
603
- }];
604
- }
605
- }
606
- if (Object.keys(file_map).length > 0) {
607
- result[classification.field_id] = file_map;
608
- }
609
- }
610
- return result;
611
- }, [classification_results, field_textbox_configs, show_tags]);
612
- // Build per-file classification metadata (document_date, document_nature)
613
- const per_field_file_metadata = useMemo(() => {
614
- if (!show_tags)
615
- return {};
616
- const result = {};
617
- for (const classification of classification_results) {
618
- if (!classification.file_classifications)
619
- continue;
620
- const meta_map = {};
621
- for (const fc of classification.file_classifications) {
622
- if (fc.document_date || fc.document_nature || fc.document_type?.length) {
623
- meta_map[fc.file_id] = {
624
- document_date: fc.document_date,
625
- document_nature: fc.document_nature,
626
- document_type: fc.document_type,
627
- };
628
- }
629
- }
630
- if (Object.keys(meta_map).length > 0) {
631
- result[classification.field_id] = meta_map;
632
- }
633
- }
634
- return result;
635
- }, [classification_results, show_tags]);
636
- // Build set of responded/resolved clarification IDs (from both pending + sent)
637
- const responded_clarification_ids = useMemo(() => {
638
- const ids = new Set();
639
- // From pending responses (not yet flushed)
640
- for (const [clar_id, response] of pending_clarification_responses) {
641
- if (response.response_choice)
642
- ids.add(clar_id);
643
- }
644
- // From sent/flushed clarifications
645
- for (const item of sent_clarifications) {
646
- if (item.status === 'responded' || item.status === 'resolved') {
647
- ids.add(item.id);
648
- }
649
- }
650
- return ids;
651
- }, [pending_clarification_responses, sent_clarifications]);
652
- // Build per-file validation errors map from file_validation_results (all errors, including responded)
653
- // — the chip needs all errors to show the response label; counting is handled separately
654
- const per_file_validation_errors = useMemo(() => {
655
- const result = {};
656
- for (const [file_id, vr] of Object.entries(file_validation_results)) {
657
- if (vr.errors && vr.errors.length > 0) {
658
- result[file_id] = vr.errors;
659
- }
660
- }
661
- return result;
662
- }, [file_validation_results]);
663
- // Set of file IDs that passed validation (excludes files with failed rule results)
664
- const validated_file_ids = useMemo(() => {
665
- const ids = new Set();
666
- for (const [file_id, vr] of Object.entries(file_validation_results)) {
667
- if (vr.status === 'passed') {
668
- const has_rule_issues = vr.rule_results?.some(r => r.issues.length > 0);
669
- if (!has_rule_issues)
670
- ids.add(file_id);
671
- }
672
- }
673
- return ids;
674
- }, [file_validation_results]);
675
- // Compute file response labels — only set when ALL errors for a file are responded to.
676
- // Used by file chips to show "N Resolved" (amber) vs "N issues" (red).
677
- const external_file_response_labels = useMemo(() => {
678
- // Get ALL errors per file (including responded ones) from file_validation_results
679
- const all_errors_by_file = {};
680
- for (const [file_id, vr] of Object.entries(file_validation_results)) {
681
- if (vr.errors && vr.errors.length > 0) {
682
- all_errors_by_file[file_id] = vr.errors;
683
- }
684
- }
685
- const labels = {};
686
- for (const [file_id, errors] of Object.entries(all_errors_by_file)) {
687
- // Check if ALL errors for this file have been responded to
688
- const all_resolved = errors.every(e => responded_clarification_ids.has(e.id));
689
- if (all_resolved) {
690
- labels[file_id] = 'resolved';
691
- }
692
- }
693
- return Object.keys(labels).length > 0 ? labels : undefined;
694
- }, [file_validation_results, responded_clarification_ids]);
695
- // Compute per-file pipeline status text from active running steps + classifying/validating state
696
- const per_file_pipeline_status = useMemo(() => {
697
- const result = {};
698
- // Pipeline steps take priority
699
- for (const [file_id, steps] of Object.entries(file_pipeline_steps)) {
700
- const active = steps.find(s => s.status === 'running');
701
- if (active) {
702
- result[file_id] = `${active.label}...`;
703
- }
704
- }
705
- // For non-pipeline files, show status from validating/classifying state (validating takes priority)
706
- for (const file_id of validating_file_ids) {
707
- if (!result[file_id]) {
708
- result[file_id] = 'Validating document...';
709
- }
710
- }
711
- for (const file_id of classifying_file_ids) {
712
- if (!result[file_id]) {
713
- result[file_id] = 'Classifying document...';
714
- }
715
- }
716
- return result;
717
- }, [file_pipeline_steps, classifying_file_ids, validating_file_ids]);
718
- // Build set of backoffice-only rule IDs to exclude from front office display
719
- const backoffice_rule_ids = useMemo(() => {
720
- const ids = new Set();
721
- for (const rule of props.validation_rules ?? []) {
722
- if (rule.check_type === 'backoffice')
723
- ids.add(rule.rule_id);
724
- }
725
- return ids;
726
- }, [props.validation_rules]);
727
- // Immediate validation clarifications enriched with any pending responses + sent responses
728
- // so ClarificationSection cards reflect selections made in the inline file dialog (and vice versa)
729
- // Excludes drafts from backoffice-only rules (those only appear in the Back Office tab)
730
- // Track which clarification responses have been explicitly confirmed by the user
731
- const [confirmed_response_ids, set_confirmed_response_ids] = useState(new Set());
732
- const immediate_clarifications = useMemo(() => {
733
- // Build lookup: sent clarifications by ID (authoritative for response data)
734
- const sent_map = new Map(sent_clarifications.map(c => [c.id, c]));
735
- const seen_ids = new Set();
736
- // Merge drafts and sent — sent version always wins when it has response data
737
- const merged = [];
738
- // 1. Process drafts, enriching with sent/pending response data
739
- for (const item of draft_clarifications) {
740
- if (item.rule_id && backoffice_rule_ids.has(item.rule_id))
741
- continue;
742
- seen_ids.add(item.id);
743
- // Sent version with response takes absolute priority
744
- const sent = sent_map.get(item.id);
745
- if (sent && (sent.status === 'responded' || sent.status === 'resolved')) {
746
- merged.push(sent);
747
- continue;
748
- }
749
- // Check pending responses (user selected + confirmed but not yet in sent)
750
- const pending = pending_clarification_responses.get(item.id);
751
- if (pending) {
752
- const is_confirmed = confirmed_response_ids.has(item.id);
753
- merged.push({
754
- ...item,
755
- status: is_confirmed ? 'responded' : item.status,
756
- response_choice: pending.response_choice ?? item.response_choice,
757
- user_comment: pending.user_comment ?? item.user_comment,
758
- response_files: pending.response_files ?? item.response_files,
759
- });
760
- continue;
761
- }
762
- merged.push(item);
763
- }
764
- // 2. Include responded/resolved sent items not in drafts (removed by dedup)
765
- for (const sent of sent_clarifications) {
766
- if (seen_ids.has(sent.id))
767
- continue;
768
- if (sent.rule_id && backoffice_rule_ids.has(sent.rule_id))
769
- continue;
770
- if (sent.status === 'responded' || sent.status === 'resolved') {
771
- merged.push(sent);
772
- }
773
- }
774
- // Read input_status from the data model to determine resolved state.
775
- // When a clarification response has input_status: 'complete' in the data model,
776
- // the card should be resolved/read-only. This is the single source of truth.
777
- const complete_ids = new Set();
778
- for (const entry of form_data_entries) {
779
- // Check nested clarification activities
780
- const walk_cd = (cd) => {
781
- if (!cd)
782
- return;
783
- for (const cdi of cd) {
784
- const inputs = Array.isArray(cdi.client_input) ? cdi.client_input : [cdi.client_input];
785
- for (const inp of inputs) {
786
- if (inp.input_status === 'complete' && inp.data_option) {
787
- // Find the clarification ID this client_data belongs to
788
- // by checking the parent clarification's question_field_id
789
- }
790
- }
791
- }
792
- };
793
- // Walk all activities for client_question types with complete client_data
794
- if (entry.client_data) {
795
- for (const cdi of entry.client_data) {
796
- const inputs = Array.isArray(cdi.client_input) ? cdi.client_input : [cdi.client_input];
797
- for (const inp of inputs) {
798
- if (inp.processed_data) {
799
- for (const pd of inp.processed_data) {
800
- if (pd.activities) {
801
- for (const act of pd.activities) {
802
- if (act.activity_info.activity_type === 'client_question' && act.clarification?.client_data) {
803
- for (const clr_cdi of act.clarification.client_data) {
804
- const clr_inputs = Array.isArray(clr_cdi.client_input) ? clr_cdi.client_input : [clr_cdi.client_input];
805
- for (const clr_inp of clr_inputs) {
806
- if (clr_inp.input_status === 'complete') {
807
- complete_ids.add(act.clarification.question.question_field_id);
808
- }
809
- }
810
- }
811
- }
812
- }
813
- }
814
- }
815
- }
816
- }
817
- }
818
- }
819
- // Also check top-level clarification entries
820
- if (entry.clarification?.client_data) {
821
- for (const cdi of entry.clarification.client_data) {
822
- const inputs = Array.isArray(cdi.client_input) ? cdi.client_input : [cdi.client_input];
823
- for (const inp of inputs) {
824
- if (inp.input_status === 'complete') {
825
- complete_ids.add(entry.clarification.question.question_field_id);
826
- }
827
- }
828
- }
829
- }
830
- }
831
- return merged.map(item => {
832
- if (item.status !== 'resolved' && item.status !== 'dismissed') {
833
- // Check data model input_status OR confirmed_response_ids (for same-session before sync)
834
- const is_complete_in_data = complete_ids.has(item.id);
835
- const is_confirmed = confirmed_response_ids.has(item.id);
836
- const sent_version = sent_map.get(item.id);
837
- // Force 'resolved' from authoritative sources:
838
- // - data model has input_status: 'complete'
839
- // - same-session confirm (OK button clicked)
840
- // - instance was submitted AND sent version has a response (client can't edit after submit)
841
- const is_submitted = active_instance?.status === 'client_submitted' || active_instance?.status === 'completed';
842
- const sent_has_response = !!(sent_version?.response_choice);
843
- if (is_complete_in_data || is_confirmed || (is_submitted && sent_has_response)) {
844
- return {
845
- ...item,
846
- status: 'resolved',
847
- response_choice: item.response_choice || sent_version?.response_choice || pending_clarification_responses.get(item.id)?.response_choice,
848
- user_comment: item.user_comment || sent_version?.user_comment || pending_clarification_responses.get(item.id)?.user_comment,
849
- };
850
- }
851
- // If sent version has a response but isn't resolved yet, preserve its actual status
852
- // so the client can still edit their response until the agent reviews it
853
- if (sent_version?.response_choice && !item.response_choice) {
854
- return {
855
- ...item,
856
- status: sent_version.status ?? 'responded',
857
- response_choice: sent_version.response_choice,
858
- user_comment: sent_version.user_comment,
859
- };
860
- }
861
- }
862
- return item;
863
- });
864
- }, [draft_clarifications, pending_clarification_responses, sent_clarifications, backoffice_rule_ids, confirmed_response_ids, form_data_entries, active_instance?.status]);
865
- const immediate_clarification_counts = useMemo(() => {
866
- const counts = { pending: 0, responded: 0, resolved: 0, dismissed: 0, total: 0 };
867
- for (const item of immediate_clarifications) {
868
- counts[item.status]++;
869
- counts.total++;
870
- }
871
- return counts;
872
- }, [immediate_clarifications]);
873
- // Compute per-file validation details for built-in FileBar dialog
874
- // Skip files where any check failed and clarifications exist — those open ClarificationDialog via on_validation_click instead
875
- const per_file_validation_details = useMemo(() => {
876
- const result = {};
877
- for (const [file_id, vr] of Object.entries(file_validation_results)) {
878
- const rr = vr.rule_results ?? [];
879
- const has_any_failures = rr.length > 0 && rr.some(r => r.issues.length > 0);
880
- if (has_any_failures) {
881
- const has_clarifications = immediate_clarifications.some(item => item.doc_references?.some(r => r.file_id === file_id));
882
- if (has_clarifications)
883
- continue; // Skip — ClarificationDialog handles this
884
- }
885
- const steps = file_pipeline_steps[file_id];
886
- result[file_id] = {
887
- file_name: vr.file_name,
888
- overall_status: has_any_failures ? 'failed'
889
- : vr.status === 'passed' || vr.status === 'skipped' ? 'passed'
890
- : vr.status === 'validating' ? 'validating'
891
- : 'failed',
892
- pipeline_steps: steps,
893
- rule_results: vr.rule_results,
894
- failure_reason: vr.failure_reason,
895
- };
896
- }
897
- return result;
898
- }, [file_validation_results, file_pipeline_steps, immediate_clarifications]);
899
- // Per-file validation badges for clarification doc references
900
- const file_validation_badges = useMemo(() => {
901
- const badges = {};
902
- for (const [file_id, vr] of Object.entries(file_validation_results)) {
903
- if (validating_file_ids.has(file_id)) {
904
- badges[file_id] = { state: 'validating' };
905
- }
906
- else if (vr.errors && vr.errors.length > 0) {
907
- const all_responded = vr.errors.every(e => responded_clarification_ids.has(e.id));
908
- if (all_responded) {
909
- badges[file_id] = { state: 'resolved', issue_count: vr.errors.length };
910
- }
911
- else {
912
- const pending_count = responded_clarification_ids.size > 0
913
- ? vr.errors.filter(e => !responded_clarification_ids.has(e.id)).length
914
- : vr.errors.length;
915
- badges[file_id] = { state: 'issues', issue_count: pending_count };
916
- }
917
- }
918
- else if (vr.rule_results && vr.rule_results.some(r => r.issues.length > 0)) {
919
- const issue_count = vr.rule_results.filter(r => r.issues.length > 0).length;
920
- badges[file_id] = { state: 'issues', issue_count };
921
- }
922
- else if (vr.status === 'passed') {
923
- badges[file_id] = { state: 'passed' };
924
- }
925
- }
926
- return Object.keys(badges).length > 0 ? badges : undefined;
927
- }, [file_validation_results, validating_file_ids, responded_clarification_ids]);
928
- // Pending clarifications for Action Required section (only pending items)
929
- // Enriched with any pending responses so the radio selection is retained after submission
930
- const pending_clarifications = useMemo(() => {
931
- return sent_clarifications
932
- .filter((c) => c.status === 'pending' || c.status === 'responded')
933
- .map(item => {
934
- const pending = pending_clarification_responses.get(item.id);
935
- if (pending) {
936
- return {
937
- ...item,
938
- status: pending.response_choice ? 'responded' : item.status,
939
- response_choice: pending.response_choice ?? item.response_choice,
940
- user_comment: pending.user_comment ?? item.user_comment,
941
- response_files: pending.response_files ?? item.response_files,
942
- };
943
- }
944
- return item;
945
- });
946
- }, [sent_clarifications, pending_clarification_responses]);
947
- // Sourced clarifications: items prepared into this instance from other instances.
948
- // Primary source: front_form_data.__clarifications (copied during prepare_for_client).
949
- // Fallback: resolve from all_clarifications by sourced_clarification_ids.
950
- // Enriched with pending responses so radio selection is retained after submission.
951
- const sourced_clarifications = useMemo(() => {
952
- if (!is_multi_instance || !active_instance)
953
- return [];
954
- // Direct source: items copied into this instance's front_form_data
955
- const direct_items = Array.isArray(active_instance.front_form_data?.['__clarifications'])
956
- ? active_instance.front_form_data['__clarifications']
957
- : [];
958
- let items;
959
- if (direct_items.length > 0) {
960
- items = direct_items;
961
- }
962
- else if (active_instance.sourced_clarification_ids?.length) {
963
- // Fallback: resolve by sourced_clarification_ids from cross-instance aggregation
964
- const sourced_ids = new Set(active_instance.sourced_clarification_ids);
965
- items = all_clarifications.filter((c) => sourced_ids.has(c.id));
966
- }
967
- else {
968
- return [];
969
- }
970
- // Enrich with pending responses — apply selection data but do NOT change status.
971
- // Status only changes to 'responded' when the user explicitly submits via the OK button.
972
- // Pending responses track in-progress selections (user may still be adding files etc.)
973
- return items.map(item => {
974
- const pending = pending_clarification_responses.get(item.id);
975
- if (pending?.response_choice && item.status === 'pending' && !item.response_choice) {
976
- return {
977
- ...item,
978
- // Keep status as-is — don't set to 'responded' from pending selection
979
- response_choice: pending.response_choice,
980
- user_comment: pending.user_comment ?? item.user_comment,
981
- response_files: pending.response_files ?? item.response_files,
982
- };
983
- }
984
- return item;
985
- });
986
- }, [is_multi_instance, active_instance, all_clarifications, pending_clarification_responses]);
987
- const pending_counts = useMemo(() => {
988
- const counts = { pending: 0, responded: 0, resolved: 0, dismissed: 0, total: 0 };
989
- for (const item of pending_clarifications) {
990
- counts[item.status]++;
991
- counts.total++;
992
- }
993
- return counts;
994
- }, [pending_clarifications]);
995
- const handle_clarification_doc_click = useCallback((file_id) => {
996
- // Find the attachment from clarification doc_references to reuse existing PDF viewer
997
- for (const item of [...pending_clarifications, ...sourced_clarifications, ...immediate_clarifications]) {
998
- const ref = item.doc_references?.find((r) => r.file_id === file_id);
999
- if (ref) {
1000
- set_viewed_file({
1001
- file_id: ref.file_id,
1002
- file_name: ref.file_name,
1003
- mime_type: ref.mime_type,
1004
- file_size: 0,
1005
- ref_id: '',
1006
- visibility: 'public',
1007
- attached_at: new Date().toISOString(),
1008
- });
1009
- return;
1010
- }
1011
- }
1012
- }, [pending_clarifications, sourced_clarifications, immediate_clarifications]);
1013
- const handle_clarification_doc_view = useCallback((file_id, file_name) => {
1014
- set_viewed_file({
1015
- file_id,
1016
- file_name,
1017
- mime_type: 'application/pdf',
1018
- file_size: 0,
1019
- ref_id: '',
1020
- visibility: 'public',
1021
- attached_at: new Date().toISOString(),
1022
- });
1023
- }, []);
1024
- // Explicit submit handler for sourced clarifications (Instance 2+ OK button)
1025
- // This persists the response AND sets status to 'responded'
1026
- const handle_sourced_respond = useCallback((id, response) => {
1027
- // Update pending state
1028
- set_pending_clarification_responses(prev => {
1029
- const next = new Map(prev);
1030
- next.set(id, response);
1031
- return next;
1032
- });
1033
- // Persist to sent_clarifications (for downstream use)
1034
- update_clarification_response(id, response);
1035
- // ALSO update front_form_data.__clarifications so the sourced_clarifications view re-renders
1036
- // AND the review queue sees the updated thread with the client's latest response
1037
- const current = Array.isArray(front_form_data?.['__clarifications'])
1038
- ? front_form_data['__clarifications']
1039
- : [];
1040
- if (current.length > 0) {
1041
- const now = new Date().toISOString();
1042
- const updated = current.map(c => {
1043
- if (c.id !== id)
1044
- return c;
1045
- // Append client response to thread so the back-office review can see it.
1046
- // The client view filters out the last client entry (shown in RESPONSE SUBMITTED instead).
1047
- const new_thread_entry = {
1048
- role: 'client',
1049
- action: 'responded',
1050
- response_choice: response.response_choice,
1051
- message: response.user_comment || undefined,
1052
- timestamp: now,
1053
- };
1054
- const existing_thread = c.thread ?? [];
1055
- return {
1056
- ...c,
1057
- status: 'responded',
1058
- response_choice: response.response_choice,
1059
- user_comment: response.user_comment,
1060
- response_files: response.response_files ?? [],
1061
- thread: [...existing_thread, new_thread_entry],
1062
- updated_at: now,
1063
- };
1064
- });
1065
- on_front_change?.('__clarifications', updated);
1066
- }
1067
- }, [set_pending_clarification_responses, update_clarification_response, front_form_data, on_front_change]);
1068
- // Stepper navigation
1069
- const go_next = useCallback(() => {
1070
- // Confirm all pending clarification responses before advancing
1071
- for (const [id, response] of pending_clarification_responses) {
1072
- if (response.response_choice) {
1073
- const is_sourced = active_instance?.sourced_clarification_ids?.includes(id);
1074
- if (is_sourced) {
1075
- handle_sourced_respond(id, response);
1076
- }
1077
- else {
1078
- // Inline confirm: mark confirmed + persist
1079
- set_confirmed_response_ids(prev => { const s = new Set(prev); s.add(id); return s; });
1080
- update_clarification_response(id, response);
1081
- }
1082
- }
1083
- }
1084
- set_completed_steps(prev => { const s = new Set(prev); s.add(active_front_step); return s; });
1085
- set_active_front_step(prev => Math.min(prev + 1, 2));
1086
- set_viewed_file(null);
1087
- }, [active_front_step, pending_clarification_responses, active_instance, handle_sourced_respond, update_clarification_response]);
1088
- const handle_step_click = useCallback((step) => {
1089
- // Mark all steps before the target as completed
1090
- set_completed_steps(prev => {
1091
- const s = new Set(prev);
1092
- for (let i = 0; i < step; i++)
1093
- s.add(i);
1094
- return s;
1095
- });
1096
- set_active_front_step(step);
1097
- set_viewed_file(null);
1098
- }, []);
1099
- // Stepper step definitions — badge shows only pending (unresolved) issues
1100
- const pending_validation_count = useMemo(() => immediate_clarifications.filter(c => c.status === 'pending').length, [immediate_clarifications]);
1101
- const stepper_steps = useMemo(() => [
1102
- { label: front_sections[0]?.section_name || 'Income & Expenses' },
1103
- { label: 'Clarifications', badge_count: pending_validation_count },
1104
- { label: 'Summary & Review' },
1105
- ], [front_sections, pending_validation_count]);
1106
- // Collect clarification responses locally (not flushed to back_form_data until submit)
1107
- // Also immediately inject response files into the file_textbox field so they appear in the form.
1108
- // Confirm a clarification response — called when user clicks OK after selecting option + adding comment/files
1109
- const handle_response_confirm = useCallback((id, direct_response) => {
1110
- const response = direct_response ?? pending_clarification_responses.get(id);
1111
- if (!response?.response_choice)
1112
- return;
1113
- // Mark as confirmed locally
1114
- set_confirmed_response_ids(prev => { const s = new Set(prev); s.add(id); return s; });
1115
- // Persist to storage
1116
- const is_sourced = active_instance?.sourced_clarification_ids?.includes(id);
1117
- if (is_sourced) {
1118
- handle_sourced_respond(id, response);
1119
- }
1120
- else {
1121
- update_clarification_response(id, response);
1122
- }
1123
- }, [pending_clarification_responses, active_instance, update_clarification_response, handle_sourced_respond]);
1124
- const handle_response_change = useCallback(async (id, response) => {
1125
- set_pending_clarification_responses(prev => {
1126
- const next = new Map(prev);
1127
- next.set(id, response);
1128
- return next;
1129
- });
1130
- // Don't persist on selection change — wait for explicit confirm (OK button).
1131
- // Response is tracked in pending_clarification_responses until confirmed.
1132
- // Reset response validation when files change
1133
- const prev_response = pending_clarification_responses.get(id);
1134
- const prev_file_count = prev_response?.response_files?.length ?? 0;
1135
- const new_file_count = response.response_files?.length ?? 0;
1136
- if (new_file_count === 0 || new_file_count !== prev_file_count) {
1137
- if (response_validations[id]) {
1138
- set_response_validations(prev => {
1139
- const next = { ...prev };
1140
- delete next[id];
1141
- return next;
1142
- });
1143
- }
1144
- // Clear the validation guard so validation can restart with the new file set
1145
- validating_ids_ref.current.delete(id);
1146
- }
1147
- // Validate and inject response files
1148
- if (response.response_files && response.response_files.length > 0) {
1149
- // Find the clarification item — check sent, draft, sourced, and all clarifications
1150
- const all_items = [...(sent_clarifications ?? []), ...(draft_clarifications ?? []), ...(all_clarifications ?? [])];
1151
- const clar_item = all_items.find(item => item.id === id);
1152
- // Check if the selected option has shows_file_upload
1153
- const selected_option = clar_item?.response_options?.find(o => o.value === response.response_choice);
1154
- const is_file_response = selected_option?.shows_file_upload === true;
1155
- // 1. VALIDATION — independent of on_front_change
1156
- if (is_file_response && clar_item && props.validation_api_endpoint && props.prompts_api_endpoint) {
1157
- const passed = await validate_clarification_response(id, clar_item, response);
1158
- if (!passed)
1159
- return; // Don't inject files on failure
1160
- }
1161
- // 2. FILE INJECTION — needs on_front_change + field_textbox_configs
1162
- if (on_front_change && field_textbox_configs) {
1163
- const source_ref = clar_item?.doc_references?.[0];
1164
- const source_field_id = source_ref?.source_field_id;
1165
- const source_file_id = source_ref?.file_id;
1166
- const field_textbox_ids = Object.keys(field_textbox_configs);
1167
- const target_field_id = source_field_id && field_textbox_ids.includes(source_field_id)
1168
- ? source_field_id
1169
- : field_textbox_ids[0];
1170
- if (!target_field_id)
1171
- return;
1172
- const current_form_data = front_form_data_ref.current;
1173
- const existing_blocks = normalize_file_textbox_value(current_form_data[target_field_id]);
1174
- const existing_file_ids = new Set(existing_blocks.filter((b) => b.type === 'file')
1175
- .map(b => b.attachment.file_id));
1176
- // Build new file blocks, skipping duplicates
1177
- const new_file_blocks = response.response_files
1178
- .filter(att => !existing_file_ids.has(att.file_id))
1179
- .map(att => ({ type: 'file', attachment: att, replaces: source_file_id }));
1180
- if (new_file_blocks.length === 0)
1181
- return;
1182
- // Insert after the source file if found, else append
1183
- const updated_blocks = [...existing_blocks];
1184
- if (source_file_id) {
1185
- const source_idx = updated_blocks.findIndex(b => b.type === 'file' && b.attachment.file_id === source_file_id);
1186
- if (source_idx >= 0) {
1187
- if (response.response_choice === 'replace_document') {
1188
- const original = updated_blocks[source_idx];
1189
- updated_blocks[source_idx] = { ...original, replaced_by: response.response_files[0].file_id };
1190
- }
1191
- updated_blocks.splice(source_idx + 1, 0, ...new_file_blocks);
1192
- }
1193
- else {
1194
- updated_blocks.push(...new_file_blocks);
1195
- }
1196
- }
1197
- else {
1198
- updated_blocks.push(...new_file_blocks);
1199
- }
1200
- on_front_change(target_field_id, updated_blocks);
1201
- }
1202
- }
1203
- }, [set_pending_clarification_responses, update_clarification_response, on_front_change, field_textbox_configs, sent_clarifications, draft_clarifications, all_clarifications, pending_clarification_responses, response_validations, props.validation_api_endpoint, props.prompts_api_endpoint, validate_clarification_response]);
1204
- // Build PDF viewer URL
1205
- const viewed_file_url = useMemo(() => {
1206
- if (!viewed_file || !file_manager?.callbacks?.get_download_url)
1207
- return null;
1208
- return file_manager.callbacks.get_download_url(viewed_file.file_id, viewed_file.visibility);
1209
- }, [viewed_file, file_manager]);
1210
- // ── Step 0: Form fields content ──
1211
- const step_0_content = (_jsxs("div", { className: "space-y-6", children: [!is_multi_instance && pending_clarifications.length > 0 && (_jsx(ClarificationSection, { items: pending_clarifications, counts: pending_counts, callbacks: { on_respond: is_front_read_only ? undefined : (id, response) => {
1212
- set_pending_clarification_responses(prev => { const next = new Map(prev); next.set(id, response); return next; });
1213
- handle_response_confirm(id, response);
1214
- } }, hide_submit: true, compact: true, collapsible: true, card_label: "Action Required", preserve_order: true, on_response_change: handle_response_change, on_doc_click: file_manager ? handle_clarification_doc_click : undefined, on_doc_view: file_manager ? handle_clarification_doc_view : undefined, file_validation: file_validation_badges, response_validations: response_validations, on_validation_click: set_validation_detail_file_id, file_validation_details: per_file_validation_details, file_pipeline_status: per_file_pipeline_status })), sourced_clarifications.length > 0 && (_jsx(ClarificationSection, { items: sourced_clarifications.map(item => {
1215
- // For responded items, strip the last client response from the thread
1216
- // since it's already shown in the RESPONSE SUBMITTED section below
1217
- if (item.status === 'responded' && item.thread?.length) {
1218
- const last = item.thread[item.thread.length - 1];
1219
- if (last.role === 'client' && last.action === 'responded') {
1220
- return { ...item, thread: item.thread.slice(0, -1) };
1221
- }
1222
- }
1223
- return item;
1224
- }), counts: {
1225
- pending: sourced_clarifications.filter(c => c.status === 'pending').length,
1226
- responded: sourced_clarifications.filter(c => c.status === 'responded').length,
1227
- resolved: sourced_clarifications.filter(c => c.status === 'resolved').length,
1228
- dismissed: sourced_clarifications.filter(c => c.status === 'dismissed').length,
1229
- total: sourced_clarifications.length,
1230
- }, callbacks: { on_respond: handle_sourced_respond }, hide_submit: true, compact: true, collapsible: true, card_label: "Action Required", preserve_order: true, on_response_change: handle_response_change, on_doc_click: file_manager ? handle_clarification_doc_click : undefined, on_doc_view: file_manager ? handle_clarification_doc_view : undefined, file_manager: file_manager?.callbacks, file_validation: file_validation_badges, response_validations: response_validations, on_validation_click: set_validation_detail_file_id, file_validation_details: per_file_validation_details, file_pipeline_status: per_file_pipeline_status })), is_multi_instance && active_instance && is_fb_terminal_status(active_instance.status) &&
1231
- (active_instance.sent_clarifications?.length ?? 0) > 0 &&
1232
- sourced_clarifications.length === 0 && (_jsx(ClarificationSection, { items: active_instance.sent_clarifications, counts: {
1233
- pending: 0,
1234
- responded: active_instance.sent_clarifications.filter(c => c.status === 'responded').length,
1235
- resolved: active_instance.sent_clarifications.filter(c => c.status === 'resolved').length,
1236
- dismissed: active_instance.sent_clarifications.filter(c => c.status === 'dismissed').length,
1237
- total: active_instance.sent_clarifications.length,
1238
- }, callbacks: {}, hide_submit: true, read_only: true, compact: true, collapsible: true, card_label: "Clarification History", preserve_order: true })), can_add_groups && front_sections.length === 0 && (_jsx(CollabFormAddFieldIcon, { field_id: "__empty_front", on_click: () => handle_open_add_group('__empty_front'), variant: "group" })), front_sections.map((section, si) => (_jsxs("div", { className: "border border-border/60 rounded-xl bg-card overflow-hidden", children: [props.show_section_headers !== false && section.section_name && (_jsx("div", { className: "px-5 pt-5 pb-3", children: _jsx("h3", { className: "text-base font-semibold text-foreground", children: section.section_name }) })), _jsxs("div", { className: "px-5 pb-5 space-y-5", children: [section.groups?.map((group, gi) => {
1239
- const fields = group.fields || [];
1240
- return (_jsxs(React.Fragment, { children: [can_add_groups && gi > 0 && (_jsx(CollabFormAddFieldIcon, { field_id: section.groups[gi - 1].id, on_click: () => handle_open_add_group(section.groups[gi - 1].id), variant: "group" })), _jsxs("div", { className: "space-y-3", children: [group.label && (_jsxs("h4", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider flex items-center gap-1.5", children: [group.label, group.ihelp_context_key && _jsx(IhelpIcon, { context_key: group.ihelp_context_key, size: 13 })] })), group.label && fields.length > 0 && group.description && (_jsx(DescriptionWithIhelp, { description: group.description, className: "text-sm text-muted-foreground/70 -mt-1" })), fields.map((field, fi) => {
1241
- const available_tags = field_textbox_configs?.[field.id]?.classification.available_tags ?? props.available_tags ?? [];
1242
- return (_jsxs(React.Fragment, { children: [can_add_fields && fi > 0 && (_jsx(CollabFormAddFieldIcon, { field_id: fields[fi - 1].id, on_click: () => handle_open_add_field(fields[fi - 1].id), variant: "field" })), _jsx("div", { children: _jsx(HazoCollabFormFileTextbox, { field_id: field.id, label: field.label, ihelp_context_key: field.ihelp_context_key, value: normalize_file_textbox_value(front_form_data[field.id]), onChange: handle_field_change(field.id), file_manager: file_manager?.callbacks, file_accept: field.file_accept, max_files: field.max_files, tag_options: show_tags ? available_tags.map((t) => t.tag_label) : undefined, classification_tags: show_tags ? per_field_file_tags[field.id] : undefined, classification_metadata: show_tags ? per_field_file_metadata[field.id] : undefined, classifying_file_ids: classifying_file_ids, queued_file_ids: queued_file_ids, autofilling_file_ids: autofilling_file_ids, on_classify_file: (attachment) => handle_classify_file(field.id, attachment), on_file_view: file_manager ? handle_file_view : undefined, placeholder: field.placeholder, validation_errors: per_file_validation_errors, validating_file_ids: validating_file_ids, validated_file_ids: validated_file_ids, on_validation_response: handle_response_change, external_file_response_labels: external_file_response_labels, responded_clarification_ids: responded_clarification_ids, sent_clarifications: sent_clarifications, on_validated_click: set_validation_detail_file_id, locked: is_front_read_only, file_validation_details: per_file_validation_details, file_pipeline_status: per_file_pipeline_status }) })] }, field.id));
1243
- }), can_add_fields && fields.length > 0 && (_jsx(CollabFormAddFieldIcon, { field_id: fields[fields.length - 1].id, on_click: () => handle_open_add_field(fields[fields.length - 1].id), variant: "field" })), can_add_fields && fields.length === 0 && (_jsx(CollabFormAddFieldIcon, { field_id: group.id, on_click: () => handle_open_add_field(group.id), variant: "field" }))] }), gi < (section.groups?.length ?? 0) - 1 && !can_add_groups && (_jsx("div", { className: "border-t border-border/40" }))] }, group.id));
1244
- }), can_add_groups && (section.groups?.length ?? 0) > 0 && (_jsx(CollabFormAddFieldIcon, { field_id: section.groups[section.groups.length - 1].id, on_click: () => handle_open_add_group(section.groups[section.groups.length - 1].id), variant: "group" }))] })] }, si))), can_add_fields && add_field_open && (_jsx(React.Suspense, { fallback: null, children: _jsx(LazyAddFieldDialog, { open: add_field_open, on_open_change: set_add_field_open, on_confirm: handle_confirm_add_fields, enable_file_textbox_tab: true, default_tab: "file_textbox", enable_library: false, title: "Add Front Office Field", file_textbox_hide_classification: true, file_textbox_hide_tags: true }) })), can_add_groups && add_group_open && (_jsx(React.Suspense, { fallback: null, children: _jsx(LazyAddGroupDialog, { open: add_group_open, on_open_change: set_add_group_open, on_confirm: handle_confirm_add_group, enable_library: false, title: "Add Front Office Group" }) }))] }));
1245
- // Check if any file has failed rule_results without corresponding clarification items
1246
- const has_unclarified_rule_failures = useMemo(() => Object.values(file_validation_results).some(vr => vr.rule_results?.some(r => r.issues.length > 0)), [file_validation_results]);
1247
- // ── Step 1: Validation Issues content ──
1248
- const step_1_content = (_jsx("div", { className: "space-y-6", children: immediate_clarifications.length > 0 ? (_jsx(ClarificationSection, { items: immediate_clarifications, counts: immediate_clarification_counts, callbacks: { on_respond: is_front_read_only ? undefined : (id, response) => {
1249
- // Store the response in pending state, then confirm it
1250
- set_pending_clarification_responses(prev => { const next = new Map(prev); next.set(id, response); return next; });
1251
- handle_response_confirm(id, response);
1252
- } }, hide_submit: false, submit_label: "Submit Response", hide_header: true, on_response_change: is_front_read_only ? undefined : handle_response_change, on_doc_click: file_manager ? handle_clarification_doc_click : undefined, on_doc_view: file_manager ? handle_clarification_doc_view : undefined, file_manager: file_manager?.callbacks, read_only: is_front_read_only, file_validation: file_validation_badges, response_validations: response_validations, on_validation_click: set_validation_detail_file_id, file_validation_details: per_file_validation_details, file_pipeline_status: per_file_pipeline_status })) : (classifying_file_ids.size > 0 || queued_file_ids.size > 0 || validating_file_ids.size > 0 || autofilling_file_ids.size > 0) ? (_jsx("div", { className: "border rounded-lg bg-card shadow-sm overflow-hidden", children: _jsxs("div", { className: "flex items-center justify-center gap-3 px-5 py-12", children: [_jsx(LuLoader, { className: "h-8 w-8 text-amber-500 animate-spin flex-shrink-0" }), _jsxs("div", { children: [_jsx("h3", { className: "text-base font-semibold text-amber-700", children: "Files are being processed" }), _jsx("p", { className: "text-sm text-muted-foreground mt-0.5", children: "Please wait for processing to complete." })] })] }) })) : has_unclarified_rule_failures ? (_jsx("div", { className: "border rounded-lg bg-card shadow-sm overflow-hidden", children: _jsxs("div", { className: "flex items-center justify-center gap-3 px-5 py-12", children: [_jsx(HiExclamationCircle, { className: "h-8 w-8 text-amber-500 flex-shrink-0" }), _jsxs("div", { children: [_jsx("h3", { className: "text-base font-semibold text-amber-700", children: "Validation issues found" }), _jsx("p", { className: "text-sm text-muted-foreground mt-0.5", children: "Some checks failed. Click the validation icon on file(s) for details." })] })] }) })) : (_jsx("div", { className: "border rounded-lg bg-card shadow-sm overflow-hidden", children: _jsxs("div", { className: "flex items-center justify-center gap-3 px-5 py-12", children: [_jsx(HiCheckCircle, { className: "h-8 w-8 text-green-500 flex-shrink-0" }), _jsxs("div", { children: [_jsx("h3", { className: "text-base font-semibold text-green-700", children: "All checks passed" }), _jsx("p", { className: "text-sm text-muted-foreground mt-0.5", children: "No validation issues found." })] })] }) })) }));
1253
- // ── Step 2: Summary & Review content ──
1254
- const step_2_content = (_jsx(SummaryReviewView, { on_file_view: file_manager ? handle_file_view : undefined, on_submitted: () => set_front_submitted(true), on_go_to_clarifications: () => set_active_front_step(1) }));
1255
- // ── Bottom action button ──
1256
- const has_pending_clarifications = sourced_clarifications.length > 0 || (!is_multi_instance && pending_clarifications.length > 0);
1257
- const show_submit_response = active_front_step === 0 && has_pending_clarifications;
1258
- const next_button = (_jsx("div", { className: "flex justify-end pt-6 pb-2", children: _jsxs("button", { type: "button", onClick: go_next, className: "inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-primary text-primary-foreground text-sm font-medium hover:bg-primary/90 transition-colors", children: [show_submit_response ? 'Submit Response' : 'Next', !show_submit_response && _jsx(HiChevronRight, { className: "h-4 w-4" })] }) }));
1259
- // ── Build form_content based on stepper mode ──
1260
- const front_office_title = props.front_office_title;
1261
- const form_content = use_stepper ? (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(FrontOfficeStepper, { steps: stepper_steps, active_step: active_front_step, completed_steps: completed_steps, on_step_click: handle_step_click }), props.front_stepper_actions && (_jsx("div", { className: "flex items-center gap-2 flex-shrink-0", children: props.front_stepper_actions }))] }), active_front_step === 0 && (_jsxs(_Fragment, { children: [step_0_content, !is_front_read_only && next_button] })), active_front_step === 1 && (_jsx(_Fragment, { children: step_1_content })), active_front_step === 2 && step_2_content, validation_detail_file_id && (() => {
1262
- const vr = file_validation_results[validation_detail_file_id];
1263
- if (!vr)
1264
- return null;
1265
- const rule_results = vr.rule_results ?? [];
1266
- const has_any_failures = rule_results.length > 0 && rule_results.some(rr => rr.issues.length > 0);
1267
- const file_clarifications = has_any_failures
1268
- ? immediate_clarifications.filter(item => item.doc_references?.some(r => r.file_id === validation_detail_file_id))
1269
- : [];
1270
- if (has_any_failures && file_clarifications.length > 0) {
1271
- const file_counts = { pending: 0, responded: 0, resolved: 0, dismissed: 0, total: 0 };
1272
- for (const item of file_clarifications) {
1273
- file_counts[item.status]++;
1274
- file_counts.total++;
1275
- }
1276
- return (_jsx(HazoUiDialog, { open: true, onOpenChange: (open) => { if (!open)
1277
- set_validation_detail_file_id(null); }, title: "Validation Issues", description: `${file_clarifications.length} issue${file_clarifications.length !== 1 ? 's' : ''} found in ${vr.file_name}`, sizeWidth: "48rem", headerBar: true, showCancelButton: false, cancelButtonText: "", actionButtonText: "Close", onConfirm: () => set_validation_detail_file_id(null), headerClassName: "[&_*]:break-all", children: _jsx(ClarificationSection, { items: file_clarifications, counts: file_counts, callbacks: { on_respond: is_front_read_only ? undefined : (id, response) => {
1278
- set_pending_clarification_responses(prev => { const next = new Map(prev); next.set(id, response); return next; });
1279
- handle_response_confirm(id, response);
1280
- } }, hide_submit: false, submit_label: "Submit Response", hide_header: true, on_response_change: is_front_read_only ? undefined : handle_response_change, on_doc_click: file_manager ? handle_clarification_doc_click : undefined, on_doc_view: file_manager ? handle_clarification_doc_view : undefined, file_manager: file_manager?.callbacks, read_only: is_front_read_only, file_validation: file_validation_badges, response_validations: response_validations, on_validation_click: set_validation_detail_file_id, file_validation_details: per_file_validation_details, file_pipeline_status: per_file_pipeline_status }) }));
1281
- }
1282
- const status_label = vr.status === 'passed' ? 'Passed' : vr.status === 'skipped' ? 'Skipped' : vr.status === 'failed' ? 'Failed' : vr.status;
1283
- return (_jsx(HazoUiDialog, { open: true, onOpenChange: (open) => { if (!open)
1284
- set_validation_detail_file_id(null); }, title: "Validation Details", description: vr.file_name, sizeWidth: "28rem", headerBar: true, showCancelButton: false, actionButtonText: "OK", onConfirm: () => set_validation_detail_file_id(null), headerClassName: "[&_*]:break-all", children: _jsx("div", { className: "space-y-3 text-sm", children: vr.status === 'validating' && rule_results.length === 0 ? (_jsxs("div", { className: "flex items-center gap-2 text-muted-foreground", children: [_jsx("span", { className: "h-4 w-4 border-2 border-muted-foreground/30 border-t-muted-foreground rounded-full animate-spin flex-shrink-0" }), "Validation in progress..."] })) : rule_results.length > 0 ? (_jsxs("div", { className: "space-y-4", children: [rule_results.some(rr => rr.issues.length > 0) && (_jsxs("div", { children: [_jsxs("span", { className: "text-[10px] font-semibold uppercase tracking-wide text-red-600", children: ["Checks Executed: Failed (", rule_results.filter(rr => rr.issues.length > 0).length, ")"] }), _jsx("div", { className: "mt-1.5 space-y-1.5", children: rule_results.filter(rr => rr.issues.length > 0).map((rr, i) => (_jsxs("div", { className: "flex items-start gap-2 rounded-md p-2 border border-destructive/30 bg-destructive/5", children: [rr.user_resolved ? (_jsx(HiCheckCircle, { className: "h-4 w-4 text-amber-500 flex-shrink-0 mt-0.5" })) : (_jsx(HiExclamationCircle, { className: "h-4 w-4 text-destructive flex-shrink-0 mt-0.5" })), _jsxs("div", { className: "min-w-0", children: [_jsx("span", { className: "font-medium", children: rr.rule_name ?? rr.rule_id }), rr.user_resolved && rr.user_resolution && (_jsxs("div", { className: "mt-1 text-xs", children: [_jsx("span", { className: "text-amber-600 font-medium", children: "Client resolved: " }), _jsx("span", { className: "text-muted-foreground", children: rr.user_resolution.response_choice }), rr.user_resolution.user_comment && (_jsxs("p", { className: "text-muted-foreground mt-0.5 italic", children: ["\u201C", rr.user_resolution.user_comment, "\u201D"] }))] })), !rr.user_resolved && (rr.summary || rr.issues[0]?.issue_description) && (_jsx("p", { className: "text-muted-foreground text-xs mt-0.5", children: rr.summary ?? rr.issues[0]?.issue_description }))] })] }, i))) })] })), rule_results.some(rr => rr.issues.length === 0) && (_jsxs("div", { children: [_jsxs("span", { className: "text-[10px] font-semibold uppercase tracking-wide text-green-600", children: ["Checks Executed: Passed (", rule_results.filter(rr => rr.issues.length === 0).length, ")"] }), _jsx("div", { className: "mt-1.5 space-y-1.5", children: rule_results.filter(rr => rr.issues.length === 0).map((rr, i) => {
1285
- let explanation;
1286
- if (rr.summary)
1287
- explanation = rr.summary;
1288
- else if (rr.issues[0]?.issue_description)
1289
- explanation = rr.issues[0].issue_description;
1290
- else if (rr.raw_response) {
1291
- try {
1292
- const parsed = JSON.parse(rr.raw_response.replace(/```(?:json)?\s*\n?/g, '').replace(/```/g, '').trim());
1293
- explanation = parsed.summary || parsed.description || parsed.reason;
1294
- }
1295
- catch { /* ignore */ }
1296
- }
1297
- return (_jsxs("div", { className: "flex items-start gap-2 rounded-md p-2 border border-green-200 bg-green-50/50", children: [_jsx(HiCheckCircle, { className: "h-4 w-4 text-green-600 flex-shrink-0 mt-0.5" }), _jsxs("div", { className: "min-w-0", children: [_jsx("span", { className: "font-medium", children: rr.rule_name ?? rr.rule_id }), explanation && (_jsx("p", { className: "text-muted-foreground text-xs mt-0.5", children: explanation }))] })] }, i));
1298
- }) })] })), vr.status === 'validating' && (_jsxs("div", { className: "flex items-center gap-2 rounded-md p-2 border border-blue-200 bg-blue-50/50", children: [_jsx("span", { className: "h-4 w-4 border-2 border-blue-300 border-t-blue-600 rounded-full animate-spin flex-shrink-0" }), _jsx("span", { className: "text-sm text-blue-700", children: "Running additional checks..." })] }))] })) : (_jsxs("div", { className: "flex items-center gap-2 text-muted-foreground", children: [_jsx(HiCheckCircle, { className: "h-4 w-4 text-green-600 flex-shrink-0" }), "No validation rules matched this file."] })) }) }));
1299
- })()] })) : (
1300
- /* Legacy scrollable layout (front_office_stepper={false}) */
1301
- _jsxs("div", { className: "space-y-6", children: [step_0_content, immediate_clarifications.length > 0 && (_jsxs("div", { className: "border border-red-200/60 rounded-xl bg-card overflow-hidden", children: [_jsxs("div", { className: "flex items-center gap-2 px-5 py-3 border-b border-red-100/80", children: [_jsx("h3", { className: "text-sm font-semibold text-red-700", children: "Clarifications" }), _jsx("span", { className: "text-[10px] text-red-600 bg-red-100 px-1.5 py-px rounded font-medium", children: immediate_clarifications.length })] }), _jsx("div", { className: "p-5", children: _jsx(ClarificationSection, { items: immediate_clarifications, counts: immediate_clarification_counts, callbacks: { on_respond: is_front_read_only ? undefined : (id, response) => {
1302
- set_pending_clarification_responses(prev => { const next = new Map(prev); next.set(id, response); return next; });
1303
- handle_response_confirm(id, response);
1304
- } }, hide_submit: false, submit_label: "Submit Response", hide_header: true, on_response_change: is_front_read_only ? undefined : handle_response_change, on_doc_click: file_manager ? handle_clarification_doc_click : undefined, on_doc_view: file_manager ? handle_clarification_doc_view : undefined, file_manager: file_manager?.callbacks, read_only: is_front_read_only, file_validation: file_validation_badges, response_validations: response_validations, on_validation_click: set_validation_detail_file_id, file_validation_details: per_file_validation_details, file_pipeline_status: per_file_pipeline_status }) })] })), _jsx(SummaryReviewView, { on_file_view: file_manager ? handle_file_view : undefined, on_submitted: () => set_front_submitted(true) }), validation_detail_file_id && (() => {
1305
- const vr = file_validation_results[validation_detail_file_id];
1306
- if (!vr)
1307
- return null;
1308
- const rule_results = vr.rule_results ?? [];
1309
- const has_any_failures = rule_results.length > 0 && rule_results.some(rr => rr.issues.length > 0);
1310
- const file_clarifications = has_any_failures
1311
- ? immediate_clarifications.filter(item => item.doc_references?.some(r => r.file_id === validation_detail_file_id))
1312
- : [];
1313
- if (has_any_failures && file_clarifications.length > 0) {
1314
- const file_counts = { pending: 0, responded: 0, resolved: 0, dismissed: 0, total: 0 };
1315
- for (const item of file_clarifications) {
1316
- file_counts[item.status]++;
1317
- file_counts.total++;
1318
- }
1319
- return (_jsx(HazoUiDialog, { open: true, onOpenChange: (open) => { if (!open)
1320
- set_validation_detail_file_id(null); }, title: "Validation Issues", description: `${file_clarifications.length} issue${file_clarifications.length !== 1 ? 's' : ''} found in ${vr.file_name}`, sizeWidth: "48rem", headerBar: true, showCancelButton: false, cancelButtonText: "", actionButtonText: "Close", onConfirm: () => set_validation_detail_file_id(null), headerClassName: "[&_*]:break-all", children: _jsx(ClarificationSection, { items: file_clarifications, counts: file_counts, callbacks: { on_respond: is_front_read_only ? undefined : (id, response) => {
1321
- set_pending_clarification_responses(prev => { const next = new Map(prev); next.set(id, response); return next; });
1322
- handle_response_confirm(id, response);
1323
- } }, hide_submit: false, submit_label: "Submit Response", hide_header: true, on_response_change: is_front_read_only ? undefined : handle_response_change, on_doc_click: file_manager ? handle_clarification_doc_click : undefined, on_doc_view: file_manager ? handle_clarification_doc_view : undefined, file_manager: file_manager?.callbacks, read_only: is_front_read_only, file_validation: file_validation_badges, response_validations: response_validations, on_validation_click: set_validation_detail_file_id, file_validation_details: per_file_validation_details, file_pipeline_status: per_file_pipeline_status }) }));
1324
- }
1325
- const status_label = vr.status === 'passed' ? 'Passed' : vr.status === 'skipped' ? 'Skipped' : vr.status === 'failed' ? 'Failed' : vr.status;
1326
- return (_jsx(HazoUiDialog, { open: true, onOpenChange: (open) => { if (!open)
1327
- set_validation_detail_file_id(null); }, title: "Validation Details", description: vr.file_name, sizeWidth: "28rem", headerBar: true, showCancelButton: false, actionButtonText: "OK", onConfirm: () => set_validation_detail_file_id(null), headerClassName: "[&_*]:break-all", children: _jsx("div", { className: "space-y-3 text-sm", children: vr.status === 'validating' && rule_results.length === 0 ? (_jsxs("div", { className: "flex items-center gap-2 text-muted-foreground", children: [_jsx("span", { className: "h-4 w-4 border-2 border-muted-foreground/30 border-t-muted-foreground rounded-full animate-spin flex-shrink-0" }), "Validation in progress..."] })) : rule_results.length > 0 ? (_jsxs("div", { className: "space-y-4", children: [rule_results.some(rr => rr.issues.length > 0) && (_jsxs("div", { children: [_jsxs("span", { className: "text-[10px] font-semibold uppercase tracking-wide text-red-600", children: ["Checks Executed: Failed (", rule_results.filter(rr => rr.issues.length > 0).length, ")"] }), _jsx("div", { className: "mt-1.5 space-y-1.5", children: rule_results.filter(rr => rr.issues.length > 0).map((rr, i) => (_jsxs("div", { className: "flex items-start gap-2 rounded-md p-2 border border-destructive/30 bg-destructive/5", children: [rr.user_resolved ? (_jsx(HiCheckCircle, { className: "h-4 w-4 text-amber-500 flex-shrink-0 mt-0.5" })) : (_jsx(HiExclamationCircle, { className: "h-4 w-4 text-destructive flex-shrink-0 mt-0.5" })), _jsxs("div", { className: "min-w-0", children: [_jsx("span", { className: "font-medium", children: rr.rule_name ?? rr.rule_id }), rr.user_resolved && rr.user_resolution && (_jsxs("div", { className: "mt-1 text-xs", children: [_jsx("span", { className: "text-amber-600 font-medium", children: "Client resolved: " }), _jsx("span", { className: "text-muted-foreground", children: rr.user_resolution.response_choice }), rr.user_resolution.user_comment && (_jsxs("p", { className: "text-muted-foreground mt-0.5 italic", children: ["\u201C", rr.user_resolution.user_comment, "\u201D"] }))] })), !rr.user_resolved && (rr.summary || rr.issues[0]?.issue_description) && (_jsx("p", { className: "text-muted-foreground text-xs mt-0.5", children: rr.summary ?? rr.issues[0]?.issue_description }))] })] }, i))) })] })), rule_results.some(rr => rr.issues.length === 0) && (_jsxs("div", { children: [_jsxs("span", { className: "text-[10px] font-semibold uppercase tracking-wide text-green-600", children: ["Checks Executed: Passed (", rule_results.filter(rr => rr.issues.length === 0).length, ")"] }), _jsx("div", { className: "mt-1.5 space-y-1.5", children: rule_results.filter(rr => rr.issues.length === 0).map((rr, i) => {
1328
- let explanation;
1329
- if (rr.summary)
1330
- explanation = rr.summary;
1331
- else if (rr.issues[0]?.issue_description)
1332
- explanation = rr.issues[0].issue_description;
1333
- else if (rr.raw_response) {
1334
- try {
1335
- const parsed = JSON.parse(rr.raw_response.replace(/```(?:json)?\s*\n?/g, '').replace(/```/g, '').trim());
1336
- explanation = parsed.summary || parsed.description || parsed.reason;
1337
- }
1338
- catch { /* ignore */ }
1339
- }
1340
- return (_jsxs("div", { className: "flex items-start gap-2 rounded-md p-2 border border-green-200 bg-green-50/50", children: [_jsx(HiCheckCircle, { className: "h-4 w-4 text-green-600 flex-shrink-0 mt-0.5" }), _jsxs("div", { className: "min-w-0", children: [_jsx("span", { className: "font-medium", children: rr.rule_name ?? rr.rule_id }), explanation && (_jsx("p", { className: "text-muted-foreground text-xs mt-0.5", children: explanation }))] })] }, i));
1341
- }) })] })), vr.status === 'validating' && (_jsxs("div", { className: "flex items-center gap-2 rounded-md p-2 border border-blue-200 bg-blue-50/50", children: [_jsx("span", { className: "h-4 w-4 border-2 border-blue-300 border-t-blue-600 rounded-full animate-spin flex-shrink-0" }), _jsx("span", { className: "text-sm text-blue-700", children: "Running additional checks..." })] }))] })) : (_jsxs("div", { className: "flex items-center gap-2 text-muted-foreground", children: [_jsx(HiCheckCircle, { className: "h-4 w-4 text-green-600 flex-shrink-0" }), "No validation rules matched this file."] })) }) }));
1342
- })()] }));
1343
- const title_element = front_office_title ? (_jsx("h3", { className: "text-sm font-medium text-muted-foreground mb-2 px-1", children: front_office_title })) : null;
1344
- // If no file is being viewed, just show the form
1345
- if (!viewed_file || !viewed_file_url) {
1346
- return (_jsxs(_Fragment, { children: [title_element, form_content] }));
1347
- }
1348
- // Side-by-side layout: form + PDF panel
1349
- return (_jsxs("div", { className: "flex gap-4 h-full", children: [_jsxs("div", { className: "flex-1 min-w-0 overflow-y-auto", children: [title_element, form_content] }), _jsx(PdfSidePanel, { file_name: viewed_file.file_name, file_url: viewed_file_url, mime_type: viewed_file.mime_type, PdfViewer: PdfViewerComponent, on_close: () => set_viewed_file(null) })] }));
1350
- }
1351
- //# sourceMappingURL=front_office_view.js.map