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,1188 +0,0 @@
1
- /**
2
- * HazoFbForm - Front/Back office form with LLM classification pipeline.
3
- *
4
- * Supports multi-instance mode (sidebar with instance switching) when `instances` prop is provided.
5
- * When `instances` is not provided, wraps flat props into a single virtual instance (backward compat).
6
- */
7
- 'use client';
8
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
9
- import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
10
- import { cn } from '../../utils/cn.js';
11
- import { use_logger } from '../../logger/index.js';
12
- import { is_fb_terminal_status } from '../../types/fb_form_instance.js';
13
- import { flat_to_data } from '../../utils/fb_data_adapter.js';
14
- import { flat_to_data_v2 } from '../../utils/fb_data_adapter_v2.js';
15
- import { set_review_decision } from '../../utils/fb_data_mutations_v2.js';
16
- import { FbFormContext } from './context.js';
17
- import { use_fb_form_state } from './hooks/use_fb_form_state.js';
18
- import { use_llm_run } from './hooks/use_llm_run.js';
19
- import { FrontOfficeView } from './views/front_office_view.js';
20
- import { BackOfficeView } from './views/back_office_view.js';
21
- import { ClientDataView } from './views/client_data_view.js';
22
- import { ClarificationsView } from './views/clarifications_view.js';
23
- import { RunDetailsDialog } from './components/run_details_dialog.js';
24
- import { BackofficeRunButton } from './components/backoffice_run_button.js';
25
- import { InstanceSidebar } from './components/instance_sidebar.js';
26
- import { register_ihelp_icon } from '../shared/ihelp_icon.js';
27
- import { AgentStepper } from './shared/agent_stepper.js';
28
- import { ReviewQueueView } from './views/review_queue_view.js';
29
- import { SendBackView } from './views/send_back_view.js';
30
- import { FileStatusAccordion } from './shared/file_status_accordion.js';
31
- export const VIRTUAL_INSTANCE_ID = '__single__';
32
- export function HazoFbForm(props) {
33
- const logger = use_logger();
34
- // Register externally provided ihelp_icon component (bypasses dynamic import)
35
- useEffect(() => {
36
- if (props.ihelp_icon)
37
- register_ihelp_icon(props.ihelp_icon);
38
- }, [props.ihelp_icon]);
39
- // ── Multi-instance resolution ──
40
- const is_multi_instance = !!props.instances && props.instances.length > 0;
41
- const [local_active_id, set_local_active_id] = useState(props.active_instance_id ?? props.instances?.[0]?.instance_id ?? VIRTUAL_INSTANCE_ID);
42
- const active_instance_id = props.active_instance_id ?? local_active_id;
43
- // Build virtual single instance when `instances` not provided
44
- const virtual_instance = useMemo(() => ({
45
- instance_id: VIRTUAL_INSTANCE_ID,
46
- label: 'Current',
47
- status: 'active',
48
- created_at: new Date().toISOString(),
49
- updated_at: new Date().toISOString(),
50
- front_sections: props.front_sections,
51
- front_form_data: props.front_form_data,
52
- }), [props.front_sections, props.front_form_data]);
53
- const instances = is_multi_instance ? props.instances : [virtual_instance];
54
- const active_instance = instances.find((i) => i.instance_id === active_instance_id) ?? instances[0] ?? null;
55
- // Build effective props scoped to the active instance
56
- // Back office (back_sections, back_form_data) stays from props — shared across instances
57
- const effective_props = useMemo(() => {
58
- if (!is_multi_instance || !active_instance)
59
- return props;
60
- return {
61
- ...props,
62
- front_sections: active_instance.front_sections,
63
- front_form_data: active_instance.front_form_data,
64
- // back_sections and back_form_data stay from props (shared)
65
- classification_results: active_instance.classification_results ?? props.classification_results,
66
- };
67
- }, [props, is_multi_instance, active_instance]);
68
- // Pending clarification responses — declared before use_fb_form_state and use_llm_run so both can access them
69
- const [pending_clarification_responses, set_pending_clarification_responses] = useState(new Map());
70
- // ── Hierarchical data model ──
71
- // When `data` prop is provided, use it as controlled state.
72
- // Otherwise maintain internal state, seeded from flat instance data.
73
- const [internal_data, set_internal_data] = useState(() => {
74
- if (props.data)
75
- return props.data;
76
- // Seed from flat model if available
77
- if (active_instance?.classification_results || active_instance?.sent_clarifications) {
78
- return flat_to_data(active_instance, 1);
79
- }
80
- return [];
81
- });
82
- // Sync controlled data prop
83
- const form_data_entries = props.data ?? internal_data;
84
- const update_form_data = useCallback((updater) => {
85
- if (props.data != null) {
86
- // Controlled: emit change, don't update internal state
87
- const next = updater(props.data);
88
- props.on_data_change?.(next);
89
- }
90
- else {
91
- // Uncontrolled: update internal state and emit
92
- set_internal_data((prev) => {
93
- const next = updater(prev);
94
- props.on_data_change?.(next);
95
- return next;
96
- });
97
- }
98
- }, [props.data, props.on_data_change]);
99
- const state = use_fb_form_state(effective_props, active_instance_id, active_instance, pending_clarification_responses, form_data_entries);
100
- const { trigger_run, trigger_complete, trigger_classify_file, trigger_assign_file, route_skipped_files, trigger_backoffice_run } = use_llm_run({
101
- props: effective_props,
102
- update_progress: state.update_progress,
103
- classification_results: state.classification_results,
104
- set_classification_results: state.set_classification_results,
105
- set_active_tab: state.set_active_tab,
106
- add_classifying_files: state.add_classifying_files,
107
- remove_classifying_files: state.remove_classifying_files,
108
- add_queued_files: state.add_queued_files,
109
- remove_queued_files: state.remove_queued_files,
110
- set_unassigned_files: state.set_unassigned_files,
111
- set_group_autofill_log: state.set_group_autofill_log,
112
- set_file_validation_results: state.set_file_validation_results,
113
- manual_review_file_ids: state.manual_review_file_ids,
114
- sent_clarifications: state.sent_clarifications,
115
- pending_clarification_responses,
116
- draft_clarifications: state.draft_clarifications,
117
- file_validation_results: state.file_validation_results,
118
- set_autofilling_file_ids: state.set_autofilling_file_ids,
119
- set_validating_file_ids: state.set_validating_file_ids,
120
- skipped_file_ids: state.skipped_file_ids,
121
- update_form_data,
122
- form_data_entries,
123
- });
124
- // Restore group_autofill_log from persisted autofill activities in the data model.
125
- // This runs on mount (and when form_data_entries changes) so that after a page refresh,
126
- // the autofill status is reconstructed from the hierarchical data.
127
- const autofill_restore_ran = useRef(false);
128
- useEffect(() => {
129
- if (autofill_restore_ran.current)
130
- return;
131
- if (form_data_entries.length === 0)
132
- return;
133
- const autofill_activities = [];
134
- // Walk all entries looking for autofill activities (v1 structure during migration)
135
- for (const entry of form_data_entries) {
136
- const walk = (items) => {
137
- if (!items)
138
- return;
139
- for (const cdi of items) {
140
- const inputs = Array.isArray(cdi.client_input) ? cdi.client_input : [cdi.client_input];
141
- for (const input of inputs) {
142
- if (input.processed_data) {
143
- for (const pd of input.processed_data) {
144
- if (pd.activities) {
145
- for (const act of pd.activities) {
146
- if (act.activity_info.activity_type === 'autofill' && act.autofill_result) {
147
- autofill_activities.push({ activity: act, pd });
148
- }
149
- }
150
- }
151
- }
152
- }
153
- }
154
- }
155
- };
156
- walk(entry.client_data);
157
- // v1 compat: check for old clarification structure during migration
158
- const v1_entry = entry;
159
- if (v1_entry.clarification?.client_data)
160
- walk(v1_entry.clarification.client_data);
161
- }
162
- if (autofill_activities.length > 0) {
163
- autofill_restore_ran.current = true;
164
- // Group activities by group_id and build the log
165
- const restored_log = {};
166
- for (const { activity } of autofill_activities) {
167
- const result = activity.autofill_result;
168
- const existing = restored_log[result.group_id];
169
- // Accumulate fields_populated across files for the same group
170
- if (existing) {
171
- existing.details = [...(existing.details ?? []), ...result.details.map(d => ({ ...d, file_name: result.file_name }))];
172
- if (result.status === 'success') {
173
- const total_fields = existing._total_fields + result.fields_populated;
174
- existing._total_fields = total_fields;
175
- existing.message = `Populated ${total_fields} field(s)`;
176
- existing.status = 'success';
177
- }
178
- }
179
- else {
180
- restored_log[result.group_id] = {
181
- status: result.status,
182
- message: result.status === 'success'
183
- ? `Populated ${result.fields_populated} field(s) from ${result.file_name}`
184
- : result.status === 'error'
185
- ? (result.error || 'Autofill failed')
186
- : 'No matching data found in document',
187
- timestamp: activity.activity_info.activity_time
188
- ? new Date(activity.activity_info.activity_time).getTime()
189
- : Date.now(),
190
- details: result.details.map(d => ({ ...d, file_name: result.file_name })),
191
- _total_fields: result.fields_populated,
192
- };
193
- }
194
- }
195
- // Clean up internal _total_fields
196
- for (const log of Object.values(restored_log)) {
197
- delete log._total_fields;
198
- }
199
- state.set_group_autofill_log(restored_log);
200
- }
201
- }, [form_data_entries]); // eslint-disable-line react-hooks/exhaustive-deps
202
- // Restore classification_results from form_data_entries when empty (e.g. Instance 2 after send-back).
203
- // Files already classified in a previous instance have tags in the data model — reconstruct
204
- // the classification results so trigger_backoffice_run can route and autofill them.
205
- const classification_restore_ran = useRef(false);
206
- useEffect(() => {
207
- if (classification_restore_ran.current)
208
- return;
209
- if (state.classification_results.length > 0)
210
- return;
211
- if (form_data_entries.length === 0)
212
- return;
213
- const field_map = new Map();
214
- const seen_file_ids = new Set();
215
- const add_file = (field_id, pd, inherit_tags) => {
216
- if (pd.processed_data_type !== 'file' || !pd.processed_data_files?.file_id)
217
- return;
218
- if (seen_file_ids.has(pd.processed_data_files.file_id))
219
- return;
220
- seen_file_ids.add(pd.processed_data_files.file_id);
221
- if (!field_map.has(field_id))
222
- field_map.set(field_id, { tags: new Set(), files: [] });
223
- const bucket = field_map.get(field_id);
224
- // Use pd tags, fall back to inherited tags from the original file in the same entry
225
- const tags = (pd.tags && pd.tags.length > 0) ? pd.tags : (inherit_tags ?? []);
226
- for (const t of tags)
227
- bucket.tags.add(t);
228
- bucket.files.push({
229
- file_id: pd.processed_data_files.file_id,
230
- file_name: pd.processed_data_files.file_name ?? '',
231
- tags,
232
- document_nature: pd.processed_data_nature?.[0],
233
- document_type: pd.processed_data_category,
234
- });
235
- };
236
- for (const entry of form_data_entries) {
237
- const field_id = entry.question?.question_field_id;
238
- if (!field_id)
239
- continue;
240
- // Collect tags from original client uploads first
241
- const entry_tags = [];
242
- for (const cdi of entry.client_data ?? []) {
243
- const input = cdi.client_input;
244
- if (!input?.processed_data)
245
- continue;
246
- for (const pd of input.processed_data) {
247
- add_file(field_id, pd);
248
- if (pd.tags)
249
- for (const t of pd.tags)
250
- if (!entry_tags.includes(t))
251
- entry_tags.push(t);
252
- }
253
- }
254
- // Files from clarification responses inherit tags from the original file
255
- for (const clr of entry.clarifications ?? []) {
256
- for (const rd of clr.response_data ?? []) {
257
- for (const rpd of rd.processed_data ?? [])
258
- add_file(field_id, rpd, entry_tags);
259
- }
260
- }
261
- }
262
- if (field_map.size > 0) {
263
- classification_restore_ran.current = true;
264
- const results = [];
265
- for (const [field_id, { tags, files }] of field_map) {
266
- results.push({ field_id, tags: Array.from(tags), file_classifications: files });
267
- }
268
- state.set_classification_results(results);
269
- logger.info('[HazoFbForm] classification_results_restored_from_data', { count: results.length, files: results.flatMap(r => r.file_classifications.map(f => f.file_id)) });
270
- }
271
- }, [form_data_entries, state.classification_results.length]); // eslint-disable-line react-hooks/exhaustive-deps
272
- // Sync flat LLM results → hierarchical data model
273
- // Rebuild data[] whenever classification, validation, or clarification state changes.
274
- // Combines ALL instances into a single data[] array (matching the reference JSON structure).
275
- // IMPORTANT: The JSON must be stable regardless of which instance is active — every instance
276
- // is built from persisted data. The active instance additionally applies live enrichments.
277
- // ── Build v2 data model from ALL instances (instance-independent) ──
278
- // Uses the flattened adapter: validations and clarifications are direct arrays,
279
- // thread captures full cross-instance conversation, no activities nesting.
280
- //
281
- // IMPORTANT: When the consumer provides persisted v2 data via `props.data`,
282
- // we DON'T overwrite it with a fresh rebuild from instances — the v2 data
283
- // is the single source of truth. We only rebuild when no v2 data exists yet
284
- // (initial build) or when classification/validation results change (new pipeline data).
285
- // ── Build v2 data model from ALL instances ──
286
- // Always rebuilds from instances. The rebuild is the truth for structure (validations,
287
- // classifications, review decisions). But client responses added via v2_add_client_response
288
- // may not be in the v1 instances yet — so we merge those thread entries in.
289
- useEffect(() => {
290
- if (instances.length === 0)
291
- return;
292
- const v2_data = flat_to_data_v2(instances);
293
- if (v2_data.length === 0)
294
- return;
295
- update_form_data(prev => {
296
- if (!prev || prev.length === 0) {
297
- return v2_data;
298
- }
299
- // Build index of existing v2 clarifications for thread merging
300
- const prev_clrs = new Map();
301
- for (const entry of prev) {
302
- for (const clr of entry.clarifications ?? []) {
303
- prev_clrs.set(clr.clarification_id, clr);
304
- }
305
- }
306
- if (prev_clrs.size === 0)
307
- return v2_data;
308
- // Use the FRESH rebuild as base (it has the latest structure from instances).
309
- // Only supplement with thread entries from existing v2 that the rebuild is missing.
310
- // This handles: v2_add_client_response added a thread entry that isn't in v1 yet.
311
- const merged = v2_data.map((entry) => {
312
- if (!entry.clarifications?.length)
313
- return entry;
314
- const merged_clrs = entry.clarifications.map((fresh_clr) => {
315
- const existing = prev_clrs.get(fresh_clr.clarification_id);
316
- if (!existing)
317
- return fresh_clr;
318
- // If existing v2 has MORE thread entries than the rebuild, the extra entries
319
- // are from v2_add_client_response and need to be preserved.
320
- const fresh_thread = fresh_clr.thread ?? [];
321
- const existing_thread = existing.thread ?? [];
322
- if (existing_thread.length > fresh_thread.length) {
323
- // Find the extra thread entries (entries in existing but not in fresh)
324
- const extra_entries = existing_thread.slice(fresh_thread.length);
325
- // Only keep client responses (the ones added by v2_add_client_response)
326
- const client_extras = extra_entries.filter((t) => t.role === 'client' && t.action === 'responded');
327
- if (client_extras.length > 0) {
328
- console.log('[DEBUG] v2 merge: appending', client_extras.length, 'client thread entries to', fresh_clr.clarification_id?.slice(-8), 'fresh_status:', fresh_clr.status);
329
- return {
330
- ...fresh_clr,
331
- thread: [...fresh_thread, ...client_extras],
332
- status: 'responded', // Client has responded
333
- review: null, // Needs fresh review
334
- response_data: existing.response_data?.length > fresh_clr.response_data?.length
335
- ? existing.response_data : fresh_clr.response_data,
336
- };
337
- }
338
- }
339
- // Preserve 'accepted' status set in the CURRENT session via set_review_decision.
340
- // Only preserve if the acceptance was made AFTER the last client response
341
- // (i.e., it's a current-session decision, not a stale one from a prior instance).
342
- if (existing.status === 'accepted' && existing.review?.thread_ref) {
343
- const merged_thread = existing_thread.length >= fresh_thread.length ? existing_thread : fresh_thread;
344
- const review_idx = merged_thread.findIndex((t) => t.thread_id === existing.review.thread_ref);
345
- const has_newer_response = merged_thread.some((t, i) => i > review_idx && t.role === 'client' && t.action === 'responded');
346
- if (!has_newer_response) {
347
- return {
348
- ...fresh_clr,
349
- thread: merged_thread,
350
- status: 'accepted',
351
- review: existing.review,
352
- response_data: existing.response_data?.length > (fresh_clr.response_data?.length ?? 0)
353
- ? existing.response_data : fresh_clr.response_data,
354
- };
355
- }
356
- }
357
- return fresh_clr;
358
- });
359
- // Preserve autofill_runs from existing data (not in v1 instances)
360
- const prev_entry = prev.find((e) => e.clarifications?.some((c) => entry.clarifications?.some((fc) => fc.clarification_id === c.clarification_id)));
361
- const merged_entry = { ...entry, clarifications: merged_clrs };
362
- if (prev_entry?.autofill_runs?.length > 0 && !merged_entry.autofill_runs?.length) {
363
- merged_entry.autofill_runs = prev_entry.autofill_runs;
364
- }
365
- return merged_entry;
366
- });
367
- return merged;
368
- });
369
- }, [instances]);
370
- // Persist classification_results to instance when they change
371
- const prev_cls_ref = useRef(state.classification_results);
372
- useEffect(() => {
373
- if (state.classification_results === prev_cls_ref.current)
374
- return;
375
- prev_cls_ref.current = state.classification_results;
376
- if (is_multi_instance && active_instance_id && state.classification_results.length > 0) {
377
- props.on_instance_update?.(active_instance_id, { classification_results: state.classification_results });
378
- }
379
- }, [state.classification_results]);
380
- // Persist review decisions and agent feedback onto the instance's sent_clarifications
381
- // so they're available when the instance is not active (stable JSON across instance switches).
382
- // IMPORTANT: Only update already-persisted sent_clarifications — do NOT write drafts here.
383
- const prev_decisions_ref = useRef(state.review_queue_decisions);
384
- const prev_comments_ref = useRef(state.send_back_comments);
385
- useEffect(() => {
386
- if (!is_multi_instance || !active_instance_id)
387
- return;
388
- if (state.review_queue_decisions === prev_decisions_ref.current && state.send_back_comments === prev_comments_ref.current)
389
- return;
390
- prev_decisions_ref.current = state.review_queue_decisions;
391
- prev_comments_ref.current = state.send_back_comments;
392
- if (state.review_queue_decisions.size === 0 && state.send_back_comments.size === 0)
393
- return;
394
- // Only enrich already-persisted sent_clarifications on the instance
395
- const current_sent = active_instance?.sent_clarifications;
396
- if (!current_sent?.length)
397
- return;
398
- let changed = false;
399
- const updated = current_sent.map(c => {
400
- let result = c;
401
- // Don't overwrite clarifications where client has already responded —
402
- // their status has progressed beyond the review decision stage
403
- if (c.status === 'responded' || c.response_choice || c.user_comment)
404
- return result;
405
- const decision = state.review_queue_decisions.get(c.id);
406
- // Match confirm_review_decisions: accept → skip_process, send_back → approve
407
- const mapped_decision = decision === 'accept' ? 'skip_process' : 'approve';
408
- if (decision && result.review_decision !== mapped_decision) {
409
- result = { ...result, review_decision: mapped_decision };
410
- changed = true;
411
- }
412
- const comment = state.send_back_comments.get(c.id);
413
- if (comment && result.agent_feedback !== comment) {
414
- result = { ...result, agent_feedback: comment };
415
- changed = true;
416
- }
417
- return result;
418
- });
419
- if (changed) {
420
- props.on_instance_update?.(active_instance_id, { sent_clarifications: updated });
421
- }
422
- }, [state.review_queue_decisions, state.send_back_comments]);
423
- // Persist file_validation_results to instance when they change
424
- const prev_fvr_ref = useRef(state.file_validation_results);
425
- useEffect(() => {
426
- if (state.file_validation_results === prev_fvr_ref.current)
427
- return;
428
- prev_fvr_ref.current = state.file_validation_results;
429
- if (is_multi_instance && active_instance_id && Object.keys(state.file_validation_results).length > 0) {
430
- props.on_instance_update?.(active_instance_id, { file_validation_results: state.file_validation_results });
431
- }
432
- }, [state.file_validation_results]);
433
- // Compute suggested document types from classification results
434
- React.useEffect(() => {
435
- const known_ids = new Set((props.available_document_types ?? []).map((dt) => dt.type_id));
436
- const all_types = new Set();
437
- for (const cls of state.classification_results) {
438
- for (const fc of cls.file_classifications) {
439
- for (const dt of fc.document_type ?? []) {
440
- if (!known_ids.has(dt))
441
- all_types.add(dt);
442
- }
443
- }
444
- }
445
- state.set_suggested_document_types([...all_types]);
446
- }, [state.classification_results, props.available_document_types]);
447
- const add_document_type = useCallback((type_id) => {
448
- const new_type = {
449
- type_id,
450
- type_label: type_id.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()),
451
- };
452
- const updated = [...(props.available_document_types ?? []), new_type];
453
- props.on_document_types_change?.(updated);
454
- state.set_suggested_document_types((prev) => prev.filter((id) => id !== type_id));
455
- }, [props]);
456
- const dismiss_suggested_type = useCallback((type_id) => {
457
- state.set_suggested_document_types((prev) => prev.filter((id) => id !== type_id));
458
- }, []);
459
- // ── Clarification lifecycle callbacks ──
460
- const send_clarifications = useCallback((items) => {
461
- logger.info('[HazoFbForm] send_clarifications', { count: items.length, ids: items.map(i => i.id), instance_id: active_instance_id });
462
- if (is_multi_instance && active_instance) {
463
- // Per-instance: merge into active instance's sent_clarifications
464
- const existing = active_instance.sent_clarifications ?? [];
465
- const updated = [...existing];
466
- for (const item of items) {
467
- const existing_idx = updated.findIndex((e) => e.id === item.id);
468
- if (existing_idx >= 0) {
469
- updated[existing_idx] = { ...item, created_at: updated[existing_idx].created_at };
470
- }
471
- else {
472
- updated.push(item);
473
- }
474
- }
475
- props.on_instance_update?.(active_instance_id, { sent_clarifications: updated });
476
- }
477
- else {
478
- // Single-instance: write to shared back_form_data
479
- const existing = Array.isArray(props.back_form_data?.['__clarifications'])
480
- ? props.back_form_data['__clarifications']
481
- : [];
482
- const updated = [...existing];
483
- for (const item of items) {
484
- const existing_idx = updated.findIndex((e) => e.id === item.id);
485
- if (existing_idx >= 0) {
486
- updated[existing_idx] = { ...item, created_at: updated[existing_idx].created_at };
487
- }
488
- else {
489
- updated.push(item);
490
- }
491
- }
492
- props.on_back_change?.('__clarifications', updated);
493
- }
494
- props.on_clarifications_sent?.(items);
495
- }, [props, is_multi_instance, active_instance, active_instance_id]);
496
- const update_clarification_status = useCallback((id, updates) => {
497
- logger.debug('[HazoFbForm] update_clarification_status', { id, status: updates.status, instance_id: active_instance_id });
498
- const now = new Date().toISOString();
499
- if (is_multi_instance && active_instance) {
500
- // Per-instance: update within active instance's sent_clarifications
501
- const existing = active_instance.sent_clarifications ?? [];
502
- let found = false;
503
- const updated = existing.map((item) => {
504
- if (item.id === id) {
505
- found = true;
506
- return { ...item, ...updates, updated_at: now };
507
- }
508
- return item;
509
- });
510
- // Upsert: if item wasn't in sent_clarifications (e.g. validation draft), add it
511
- if (!found) {
512
- const draft = state.draft_clarifications.find((d) => d.id === id);
513
- if (draft) {
514
- updated.push({ ...draft, ...updates, updated_at: now });
515
- }
516
- else {
517
- // Fallback: search all validation errors for the item (draft may have been deduped)
518
- for (const vr of Object.values(state.file_validation_results)) {
519
- const err = vr.errors?.find((e) => e.id === id);
520
- if (err) {
521
- updated.push({ ...err, ...updates, updated_at: now });
522
- break;
523
- }
524
- }
525
- // Last resort: create minimal item from updates
526
- if (!updated.some((c) => c.id === id)) {
527
- updated.push({ id, type: 'validation_override', status: 'pending', target_field_id: '', target_label: '', issue_description: '', doc_references: [], response_options: [], created_at: now, ...updates, updated_at: now });
528
- }
529
- }
530
- }
531
- // Also update front_form_data.__clarifications if the item lives there
532
- const front_clrs = active_instance.front_form_data?.['__clarifications'];
533
- const inst_updates = { sent_clarifications: updated };
534
- if (Array.isArray(front_clrs) && front_clrs.some((c) => c.id === id)) {
535
- inst_updates.front_form_data = {
536
- ...active_instance.front_form_data,
537
- __clarifications: front_clrs.map(c => c.id === id ? { ...c, ...updates, updated_at: now } : c),
538
- };
539
- }
540
- logger.info('[HazoFbForm] update_clarification_status → on_instance_update', {
541
- instance_id: active_instance_id,
542
- sent_clrs_count: inst_updates.sent_clarifications?.length,
543
- found_in_existing: found,
544
- });
545
- props.on_instance_update?.(active_instance_id, inst_updates);
546
- }
547
- else {
548
- // Single-instance: update in shared back_form_data
549
- const existing = Array.isArray(props.back_form_data?.['__clarifications'])
550
- ? props.back_form_data['__clarifications']
551
- : [];
552
- let found = false;
553
- const updated = existing.map((item) => {
554
- if (item.id === id) {
555
- found = true;
556
- return { ...item, ...updates, updated_at: now };
557
- }
558
- return item;
559
- });
560
- if (!found) {
561
- const draft = state.draft_clarifications.find((d) => d.id === id);
562
- if (draft) {
563
- updated.push({ ...draft, ...updates, updated_at: now });
564
- }
565
- else {
566
- for (const vr of Object.values(state.file_validation_results)) {
567
- const err = vr.errors?.find((e) => e.id === id);
568
- if (err) {
569
- updated.push({ ...err, ...updates, updated_at: now });
570
- break;
571
- }
572
- }
573
- if (!updated.some((c) => c.id === id)) {
574
- updated.push({ id, type: 'validation_override', status: 'pending', target_field_id: '', target_label: '', issue_description: '', doc_references: [], response_options: [], created_at: now, ...updates, updated_at: now });
575
- }
576
- }
577
- }
578
- props.on_back_change?.('__clarifications', updated);
579
- }
580
- }, [props, is_multi_instance, active_instance, active_instance_id, state.draft_clarifications, state.file_validation_results]);
581
- const update_clarification_response = useCallback((id, response) => {
582
- const now = new Date().toISOString();
583
- // Find the base item from ALL available sources to ensure we have the full data
584
- const find_base = () => {
585
- // 1. Sent clarifications on instance
586
- const sent = active_instance?.sent_clarifications?.find(c => c.id === id);
587
- if (sent)
588
- return sent;
589
- // 2. Draft clarifications (in-memory)
590
- const draft = state.draft_clarifications.find(d => d.id === id);
591
- if (draft)
592
- return draft;
593
- // 3. File validation results errors
594
- for (const vr of Object.values(state.file_validation_results)) {
595
- const err = vr.errors?.find(e => e.id === id);
596
- if (err)
597
- return err;
598
- }
599
- // 4. Back form data clarifications
600
- const back_clrs = Array.isArray(props.back_form_data?.['__clarifications'])
601
- ? props.back_form_data['__clarifications']
602
- : [];
603
- const back = back_clrs.find(c => c.id === id);
604
- if (back)
605
- return back;
606
- // 5. Front form data clarifications
607
- const front_clrs = Array.isArray(active_instance?.front_form_data?.['__clarifications'])
608
- ? active_instance.front_form_data['__clarifications']
609
- : [];
610
- return front_clrs.find(c => c.id === id);
611
- };
612
- const base = find_base();
613
- // Build thread entry for the client's response
614
- const existing_thread = base?.thread ?? [];
615
- const client_thread_entry = {
616
- role: 'client',
617
- action: 'responded',
618
- response_choice: response.response_choice,
619
- message: response.user_comment || undefined,
620
- files: response.response_files?.map(f => ({
621
- file_id: f.file_id,
622
- ref_id: f.file_id,
623
- file_name: f.file_name,
624
- file_size: f.file_size ?? 0,
625
- mime_type: f.mime_type ?? 'application/octet-stream',
626
- visibility: 'public',
627
- attached_at: now,
628
- })),
629
- timestamp: now,
630
- };
631
- // Only add if not already in thread (avoid duplicates from handle_sourced_respond)
632
- const already_in_thread = existing_thread.some(t => t.role === 'client' && t.action === 'responded' && t.timestamp === now);
633
- const updated_thread = already_in_thread ? existing_thread : [...existing_thread, client_thread_entry];
634
- const updates = {
635
- status: 'responded',
636
- response_choice: response.response_choice,
637
- user_comment: response.user_comment,
638
- response_files: response.response_files ?? [],
639
- updated_at: now,
640
- thread: updated_thread,
641
- };
642
- const merged = base
643
- ? { ...base, ...updates }
644
- : { id, type: 'validation_override', target_field_id: '', target_label: '', issue_description: '', doc_references: [], response_options: [], created_at: now, ...updates };
645
- if (is_multi_instance && active_instance) {
646
- // Upsert into sent_clarifications on the instance
647
- const existing = active_instance.sent_clarifications ?? [];
648
- const found = existing.some(c => c.id === id);
649
- const updated_sent = found
650
- ? existing.map(c => c.id === id ? merged : c)
651
- : [...existing, merged];
652
- const inst_updates = { sent_clarifications: updated_sent };
653
- // Also update front_form_data.__clarifications if needed
654
- const front_clrs = active_instance.front_form_data?.['__clarifications'];
655
- if (Array.isArray(front_clrs) && front_clrs.some((c) => c.id === id)) {
656
- inst_updates.front_form_data = {
657
- ...active_instance.front_form_data,
658
- __clarifications: front_clrs.map(c => c.id === id ? merged : c),
659
- };
660
- }
661
- props.on_instance_update?.(active_instance_id, inst_updates);
662
- // Note: v2 data model will be updated by the useEffect([instances]) rebuild
663
- // triggered by the on_instance_update call above. The rebuild uses the
664
- // updated instance data which includes the response.
665
- }
666
- else {
667
- // Single-instance: update in back_form_data
668
- const existing = Array.isArray(props.back_form_data?.['__clarifications'])
669
- ? props.back_form_data['__clarifications']
670
- : [];
671
- const found = existing.some(c => c.id === id);
672
- const updated = found
673
- ? existing.map(c => c.id === id ? merged : c)
674
- : [...existing, merged];
675
- props.on_back_change?.('__clarifications', updated);
676
- }
677
- }, [is_multi_instance, active_instance, active_instance_id, props, state.draft_clarifications, state.file_validation_results]);
678
- /** Batch-update multiple responses in a single write to avoid stale-state overwrites */
679
- const batch_update_clarification_responses = useCallback((responses) => {
680
- if (responses.size === 0)
681
- return;
682
- const now = new Date().toISOString();
683
- const apply_response = (item, response) => ({
684
- ...item,
685
- status: 'responded',
686
- response_choice: response.response_choice,
687
- user_comment: response.user_comment,
688
- response_files: response.response_files ?? [],
689
- updated_at: now,
690
- });
691
- if (is_multi_instance && active_instance) {
692
- const existing = active_instance.sent_clarifications ?? [];
693
- const matched_ids = new Set();
694
- const updated = existing.map((item) => {
695
- const response = responses.get(item.id);
696
- if (!response)
697
- return item;
698
- matched_ids.add(item.id);
699
- return apply_response(item, response);
700
- });
701
- // Upsert: append draft items that weren't in sent_clarifications
702
- for (const [id, response] of responses) {
703
- if (matched_ids.has(id))
704
- continue;
705
- const draft = state.draft_clarifications.find((d) => d.id === id);
706
- if (draft)
707
- updated.push(apply_response(draft, response));
708
- }
709
- props.on_instance_update?.(active_instance_id, { sent_clarifications: updated });
710
- }
711
- else {
712
- const existing = Array.isArray(props.back_form_data?.['__clarifications'])
713
- ? props.back_form_data['__clarifications']
714
- : [];
715
- const matched_ids = new Set();
716
- const updated = existing.map((item) => {
717
- const response = responses.get(item.id);
718
- if (!response)
719
- return item;
720
- matched_ids.add(item.id);
721
- return apply_response(item, response);
722
- });
723
- for (const [id, response] of responses) {
724
- if (matched_ids.has(id))
725
- continue;
726
- const draft = state.draft_clarifications.find((d) => d.id === id);
727
- if (draft)
728
- updated.push(apply_response(draft, response));
729
- }
730
- props.on_back_change?.('__clarifications', updated);
731
- }
732
- }, [props, is_multi_instance, active_instance, active_instance_id, state.draft_clarifications]);
733
- const resolve_clarification = useCallback((id) => {
734
- update_clarification_status(id, { status: 'resolved' });
735
- }, [update_clarification_status]);
736
- const dismiss_clarification = useCallback((id) => {
737
- update_clarification_status(id, { status: 'dismissed' });
738
- }, [update_clarification_status]);
739
- const reopen_clarification = useCallback((id) => {
740
- update_clarification_status(id, { status: 'pending' });
741
- }, [update_clarification_status]);
742
- // ── All clarifications across instances ──
743
- const all_clarifications = useMemo(() => {
744
- if (is_multi_instance) {
745
- return instances.flatMap((inst) => inst.sent_clarifications ?? []);
746
- }
747
- // Single-instance: read from shared back_form_data
748
- const raw = props.back_form_data?.['__clarifications'];
749
- if (Array.isArray(raw))
750
- return raw;
751
- return [];
752
- }, [is_multi_instance, instances, props.back_form_data]);
753
- // ── Multi-instance: instance switching ──
754
- const set_active_instance = useCallback((instance_id) => {
755
- set_local_active_id(instance_id);
756
- props.on_instance_change?.(instance_id);
757
- }, [props.on_instance_change]);
758
- const create_instance = useCallback(async () => {
759
- if (!props.on_instance_create)
760
- return null;
761
- const new_instance = await props.on_instance_create();
762
- set_active_instance(new_instance.instance_id);
763
- return new_instance;
764
- }, [props.on_instance_create, set_active_instance]);
765
- // ── Prepare for client: move clarifications to target instance ──
766
- const prepare_for_client = useCallback(async (clarification_ids, sent_items) => {
767
- logger.info('[HazoFbForm] prepare_for_client', { clarification_count: clarification_ids.length, source_instance: active_instance_id });
768
- if (clarification_ids.length === 0)
769
- return;
770
- // Find or create target instance
771
- let target_id = null;
772
- if (is_multi_instance) {
773
- // Look for a draft/active instance that isn't the current one
774
- const target = instances.find((i) => i.instance_id !== active_instance_id && (i.status === 'draft' || i.status === 'active' || i.status === 'sent_to_client'));
775
- if (target) {
776
- target_id = target.instance_id;
777
- }
778
- else if (props.on_instance_create) {
779
- // Create new instance WITHOUT switching to it — user stays on current instance
780
- const new_inst = await props.on_instance_create();
781
- target_id = new_inst.instance_id;
782
- // Do NOT call set_active_instance here
783
- }
784
- }
785
- if (target_id) {
786
- if (is_multi_instance) {
787
- const existing = active_instance?.sent_clarifications ?? [];
788
- // Merge: start with existing, replacing any items that appear in sent_items (which have cleared response data)
789
- const sent_items_map = new Map((sent_items ?? []).map(item => [item.id, item]));
790
- const merged = existing.map(e => sent_items_map.get(e.id) ?? e);
791
- // Add any new sent_items not already in existing
792
- if (sent_items) {
793
- for (const item of sent_items) {
794
- if (!merged.find(e => e.id === item.id))
795
- merged.push(item);
796
- }
797
- }
798
- // Step 1: Write items with status: 'pending' so consuming app can read them for forwarding
799
- props.on_instance_update?.(active_instance_id, { sent_clarifications: merged });
800
- // Step 2: Forward cleared items directly to the target instance
801
- // Pass sent_items (which have response_choice/user_comment cleared) so consuming app
802
- // doesn't need to read from state (avoids stale data from React state batching)
803
- props.on_prepare_for_client?.(clarification_ids, target_id, sent_items);
804
- // Step 3: Clear pending_clarification_responses for forwarded IDs
805
- // This prevents stale responses from Instance 1 polluting Instance 2's view
806
- set_pending_clarification_responses(prev => {
807
- const next = new Map(prev);
808
- for (const id of clarification_ids)
809
- next.delete(id);
810
- return next.size === prev.size ? prev : next;
811
- });
812
- // Step 4: Mark forwarded items as resolved on source and complete the instance.
813
- // Strip author/rejected thread entries — those are back-office actions only visible on the target instance.
814
- const forwarded_set = new Set(clarification_ids);
815
- const now = new Date().toISOString();
816
- const resolved = merged.map((item) => {
817
- if (!forwarded_set.has(item.id))
818
- return item;
819
- const clean_thread = item.thread?.filter(t => !(t.role === 'author' && t.action === 'rejected'));
820
- return { ...item, status: 'resolved', updated_at: now, thread: clean_thread };
821
- });
822
- props.on_instance_update?.(active_instance_id, { sent_clarifications: resolved, status: 'completed' });
823
- }
824
- else {
825
- props.on_prepare_for_client?.(clarification_ids, target_id);
826
- }
827
- }
828
- // Remove sent items from drafts
829
- state.set_draft_clarifications((prev) => prev.filter((d) => !clarification_ids.includes(d.id)));
830
- }, [is_multi_instance, instances, active_instance, active_instance_id, props, state.set_draft_clarifications]);
831
- // ── Reject & send back ──
832
- const reject_clarification = useCallback((id, reason) => {
833
- logger.info('[HazoFbForm] reject_clarification', { id, reason, instance_id: active_instance_id });
834
- // Find the clarification item from sent or draft clarifications
835
- const all_items = [
836
- ...(is_multi_instance ? (active_instance?.sent_clarifications ?? []) : []),
837
- ...(Array.isArray(props.back_form_data?.['__clarifications'])
838
- ? props.back_form_data['__clarifications']
839
- : []),
840
- ...state.draft_clarifications,
841
- ];
842
- const item = all_items.find((c) => c.id === id);
843
- if (!item) {
844
- logger.warn('[HazoFbForm] reject_clarification: item not found', { id });
845
- return;
846
- }
847
- // Build thread from existing data if not present
848
- const now = new Date().toISOString();
849
- const existing_thread = item.thread ?? [
850
- { role: 'system', action: 'created', message: item.issue_description, timestamp: item.created_at },
851
- ...(item.response_choice ? [{
852
- role: 'client',
853
- action: 'responded',
854
- response_choice: item.response_choice,
855
- message: item.user_comment,
856
- files: item.response_files?.length ? item.response_files : undefined,
857
- timestamp: item.updated_at ?? now,
858
- }] : []),
859
- ];
860
- // Append author rejection entry
861
- const thread = [
862
- ...existing_thread,
863
- {
864
- role: 'author',
865
- action: 'rejected',
866
- message: reason,
867
- timestamp: now,
868
- actor_name: props.current_user?.name,
869
- },
870
- ];
871
- // Update the clarification: reset response, set pending, attach thread
872
- const updates = {
873
- status: 'pending',
874
- response_choice: undefined,
875
- user_comment: undefined,
876
- response_files: [],
877
- thread,
878
- updated_at: now,
879
- };
880
- // Write updated item back
881
- if (is_multi_instance && active_instance) {
882
- const existing = active_instance.sent_clarifications ?? [];
883
- const updated = existing.map((c) => c.id === id ? { ...c, ...updates } : c);
884
- props.on_instance_update?.(active_instance_id, { sent_clarifications: updated });
885
- }
886
- else {
887
- const existing = Array.isArray(props.back_form_data?.['__clarifications'])
888
- ? props.back_form_data['__clarifications']
889
- : [];
890
- const updated = existing.map((c) => c.id === id ? { ...c, ...updates } : c);
891
- props.on_back_change?.('__clarifications', updated);
892
- }
893
- // Forward to new instance via prepare_for_client
894
- const updated_item = { ...item, ...updates };
895
- prepare_for_client([id], [updated_item]);
896
- }, [props, is_multi_instance, active_instance, active_instance_id, prepare_for_client, state.draft_clarifications]);
897
- // ── Override resolved validation — create draft clarification (pre-approved) ──
898
- const create_validation_clarification = useCallback((rule, file, message) => {
899
- logger.info('[HazoFbForm] create_validation_clarification', { rule_id: rule.rule_id, file_id: file.file_id });
900
- const now = new Date().toISOString();
901
- const id = `clr_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
902
- const new_item = {
903
- id,
904
- type: 'validation_override',
905
- status: 'pending',
906
- target_field_id: '',
907
- target_label: '',
908
- rule_id: rule.rule_id,
909
- rule_name: rule.rule_name,
910
- issue_description: message,
911
- validation_details: rule.summary ?? rule.issues[0]?.issue_description ?? rule.raw_response ?? '',
912
- doc_references: [{
913
- file_id: file.file_id,
914
- file_name: file.file_name,
915
- mime_type: 'application/pdf',
916
- comment: file.classification,
917
- }],
918
- response_options: [
919
- { value: 'upload_corrected', label: 'Upload corrected document', shows_file_upload: true },
920
- { value: 'provide_comment', label: 'Provide comment', shows_additional_input: true },
921
- ],
922
- response_files: [],
923
- created_at: now,
924
- created_by: props.current_user?.id ?? props.current_user?.name,
925
- };
926
- // Add as draft clarification (not sent yet)
927
- state.set_draft_clarifications((prev) => [...prev, new_item]);
928
- // Pre-set review decision to 'approve'
929
- state.set_review_decisions((prev) => {
930
- const next = new Map(prev);
931
- next.set(id, 'approve');
932
- return next;
933
- });
934
- // Mark the rule as sent_back in validation results (prevents re-sending + changes badge)
935
- const vr_copy = { ...state.file_validation_results };
936
- const file_vr = vr_copy[file.file_id];
937
- if (file_vr?.rule_results) {
938
- const updated_rules = file_vr.rule_results.map((rr) => rr.rule_id === rule.rule_id ? { ...rr, sent_back: true, sent_back_message: message } : rr);
939
- vr_copy[file.file_id] = { ...file_vr, rule_results: updated_rules };
940
- state.set_file_validation_results(vr_copy);
941
- }
942
- }, [props, state.set_draft_clarifications, state.set_review_decisions, state.file_validation_results, state.set_file_validation_results]);
943
- // ── Accept resolved validation rule ──
944
- const accept_validation_rule = useCallback((rule, file_id) => {
945
- logger.info('[HazoFbForm] accept_validation_rule', { rule_id: rule.rule_id, file_id });
946
- const vr_copy = { ...state.file_validation_results };
947
- const file_vr = vr_copy[file_id];
948
- if (file_vr?.rule_results) {
949
- const updated_rules = file_vr.rule_results.map((rr) => rr.rule_id === rule.rule_id ? { ...rr, accepted: true } : rr);
950
- vr_copy[file_id] = { ...file_vr, rule_results: updated_rules };
951
- state.set_file_validation_results(vr_copy);
952
- }
953
- }, [state.file_validation_results, state.set_file_validation_results]);
954
- // ── Accept all unreviewed resolved rules on a file ──
955
- const accept_all_validation_rules = useCallback((rules, file_id) => {
956
- logger.info('[HazoFbForm] accept_all_validation_rules', { rule_ids: rules.map(r => r.rule_id), file_id });
957
- const rule_ids = new Set(rules.map(r => r.rule_id));
958
- const vr_copy = { ...state.file_validation_results };
959
- const file_vr = vr_copy[file_id];
960
- if (file_vr?.rule_results) {
961
- const updated_rules = file_vr.rule_results.map((rr) => rule_ids.has(rr.rule_id) ? { ...rr, accepted: true } : rr);
962
- vr_copy[file_id] = { ...file_vr, rule_results: updated_rules };
963
- state.set_file_validation_results(vr_copy);
964
- }
965
- }, [state.file_validation_results, state.set_file_validation_results]);
966
- // Badge: reviewed/total for draft clarifications (excluding those with pending responses)
967
- const clarification_badge = useMemo(() => {
968
- const active_drafts = state.draft_clarifications.filter((d) => !pending_clarification_responses.get(d.id)?.response_choice);
969
- const total = active_drafts.length;
970
- const reviewed = active_drafts.filter((d) => state.review_decisions.has(d.id)).length;
971
- // Also count pending sent items in single-instance mode
972
- const sent_pending = !is_multi_instance
973
- ? state.sent_clarifications.filter((c) => c.status === 'pending' || c.status === 'responded').length
974
- : 0;
975
- return { total: total + sent_pending, reviewed, all_reviewed: total > 0 && reviewed === total };
976
- }, [state.draft_clarifications, state.review_decisions, state.sent_clarifications, is_multi_instance, pending_clarification_responses]);
977
- // ── User role checks ──
978
- const is_responder = props.user_role === 'responder';
979
- const show_back_office = !is_responder;
980
- const is_past_instance = active_instance ? is_fb_terminal_status(active_instance.status) : false;
981
- // Guard: if responder somehow has a non-front tab active, reset to front
982
- useEffect(() => {
983
- if (is_responder && (state.active_tab === 'clarifications' || state.active_tab === 'client_data' || state.active_tab === 'back')) {
984
- state.set_active_tab('front');
985
- }
986
- }, [is_responder, state.active_tab]);
987
- // Agent stepper redirect: when enabled, redirect legacy back-office tabs to stepper
988
- useEffect(() => {
989
- if (props.agent_stepper && show_back_office && ['client_data', 'back', 'clarifications'].includes(state.active_tab)) {
990
- state.set_active_tab('agent_stepper');
991
- }
992
- }, [props.agent_stepper, show_back_office, state.active_tab]);
993
- // ── Front office stepper state (persists across tab switches) ──
994
- const [active_front_step, set_active_front_step] = useState(0);
995
- // ── Submit state (lifted from SummaryReviewView so it persists across tab switches) ──
996
- // Re-derive when active instance changes so each instance has its own submitted status
997
- const [submitted_at, set_submitted_at] = useState(null);
998
- useEffect(() => {
999
- if (active_instance) {
1000
- const s = active_instance.status;
1001
- if (s === 'client_submitted' || s === 'client_submitted_auto_review' || s === 'completed') {
1002
- set_submitted_at(active_instance.updated_at ? new Date(active_instance.updated_at) : new Date());
1003
- }
1004
- else {
1005
- set_submitted_at(null);
1006
- }
1007
- }
1008
- else {
1009
- set_submitted_at(null);
1010
- }
1011
- }, [active_instance_id]);
1012
- // Status dialog for run details
1013
- const [details_open, set_details_open] = useState(false);
1014
- const is_running = state.run_progress.status === 'classifying' || state.run_progress.status === 'validating' || state.run_progress.status === 'routing';
1015
- // Debug dialogs default to true
1016
- const show_debug_dialogs = props.show_debug_dialogs !== false;
1017
- // Show BackofficeRunButton in tab bar when author has LLM pipeline configured
1018
- const show_backoffice_run_button = !is_responder && !!props.llm_api_endpoint && !!props.field_textbox_configs;
1019
- // Determine if front form has any file data (for disabling Process Files when no data)
1020
- const has_front_data = useMemo(() => {
1021
- const data = effective_props.front_form_data;
1022
- if (!data)
1023
- return false;
1024
- return Object.entries(data).some(([key, val]) => {
1025
- // File keys count as data (client may submit files only)
1026
- if (key.startsWith('__files_') || key.startsWith('__private_files_')) {
1027
- if (Array.isArray(val) && val.length > 0)
1028
- return true;
1029
- return false;
1030
- }
1031
- if (key.startsWith('__'))
1032
- return false; // Skip other internal keys
1033
- if (val === undefined || val === null || val === '')
1034
- return false;
1035
- if (Array.isArray(val) && val.length === 0)
1036
- return false;
1037
- return true;
1038
- });
1039
- }, [effective_props.front_form_data]);
1040
- // Send to Client button: show when callback is provided, instance is not past/terminal, and on front tab
1041
- const can_send_to_client = !is_past_instance && !!props.on_send_to_client;
1042
- // Wrap confirm_review_decisions to also persist accept/send_back to form_data_entries (v2 data model)
1043
- const confirm_review_decisions_with_data = useCallback(() => {
1044
- // Persist ALL decisions in a single update_form_data call to avoid stale-state overwrites
1045
- // (controlled mode reads from props.data which doesn't change between calls)
1046
- const current_user = props.current_user;
1047
- const decisions = Array.from(state.review_queue_decisions.entries());
1048
- if (decisions.length > 0) {
1049
- update_form_data(prev => {
1050
- let data = prev;
1051
- for (const [item_id, decision] of decisions) {
1052
- const comment = decision === 'send_back' ? state.send_back_comments.get(item_id) : undefined;
1053
- data = set_review_decision(data, item_id, decision, comment, current_user?.id, current_user?.name);
1054
- }
1055
- return data;
1056
- });
1057
- }
1058
- // Then run the original confirm logic (UI state, file routing, step advance)
1059
- return state.confirm_review_decisions();
1060
- }, [state.review_queue_decisions, state.send_back_comments, state.confirm_review_decisions, update_form_data, props.current_user]);
1061
- const ctx = {
1062
- props: effective_props,
1063
- active_tab: state.active_tab,
1064
- set_active_tab: state.set_active_tab,
1065
- run_progress: state.run_progress,
1066
- classification_results: state.classification_results,
1067
- trigger_run,
1068
- trigger_complete,
1069
- trigger_backoffice_run,
1070
- trigger_classify_file,
1071
- classifying_file_ids: state.classifying_file_ids,
1072
- queued_file_ids: state.queued_file_ids,
1073
- unassigned_files: state.unassigned_files,
1074
- trigger_assign_file,
1075
- set_unassigned_files: state.set_unassigned_files,
1076
- group_autofill_log: state.group_autofill_log,
1077
- file_validation_results: state.file_validation_results,
1078
- set_file_validation_results: state.set_file_validation_results,
1079
- validating_file_ids: state.validating_file_ids,
1080
- set_validating_file_ids: state.set_validating_file_ids,
1081
- suggested_document_types: state.suggested_document_types,
1082
- add_document_type,
1083
- dismiss_suggested_type,
1084
- draft_clarifications: state.draft_clarifications,
1085
- set_draft_clarifications: state.set_draft_clarifications,
1086
- sent_clarifications: state.sent_clarifications,
1087
- send_clarifications,
1088
- update_clarification_response,
1089
- batch_update_clarification_responses,
1090
- resolve_clarification,
1091
- dismiss_clarification,
1092
- reopen_clarification,
1093
- // Multi-instance
1094
- instances,
1095
- active_instance_id,
1096
- active_instance,
1097
- set_active_instance,
1098
- create_instance,
1099
- is_multi_instance,
1100
- // Icons behaviour
1101
- icons_behaviour: props.icons_behaviour,
1102
- active_role: props.active_role,
1103
- user_role: props.user_role,
1104
- current_user: props.current_user,
1105
- // Clarification flow
1106
- all_clarifications,
1107
- prepare_for_client,
1108
- // Pending clarification responses
1109
- pending_clarification_responses,
1110
- set_pending_clarification_responses,
1111
- // Manual review file IDs
1112
- manual_review_file_ids: state.manual_review_file_ids,
1113
- set_manual_review_file_ids: state.set_manual_review_file_ids,
1114
- // Route skipped files
1115
- route_skipped_files,
1116
- // Review decisions
1117
- review_decisions: state.review_decisions,
1118
- set_review_decisions: state.set_review_decisions,
1119
- // Ignored / Skipped file IDs
1120
- ignored_file_ids: state.ignored_file_ids,
1121
- set_ignored_file_ids: state.set_ignored_file_ids,
1122
- skipped_file_ids: state.skipped_file_ids,
1123
- set_skipped_file_ids: state.set_skipped_file_ids,
1124
- // Autofill progress
1125
- autofilling_file_ids: state.autofilling_file_ids,
1126
- // Back-office group collapse
1127
- back_group_collapsed: state.back_group_collapsed,
1128
- set_back_group_collapsed: state.set_back_group_collapsed,
1129
- // Front office stepper
1130
- active_front_step,
1131
- set_active_front_step,
1132
- // Submit state
1133
- submitted_at,
1134
- set_submitted_at,
1135
- // Agent stepper state
1136
- active_step: state.active_step,
1137
- set_active_step: state.set_active_step,
1138
- step_configs: state.step_configs,
1139
- review_queue_items: state.review_queue_items,
1140
- review_queue_decisions: state.review_queue_decisions,
1141
- set_review_queue_decisions: state.set_review_queue_decisions,
1142
- review_complete: state.review_complete,
1143
- send_back_items: state.send_back_items,
1144
- send_back_comments: state.send_back_comments,
1145
- set_send_back_comments: state.set_send_back_comments,
1146
- confirm_review_decisions: confirm_review_decisions_with_data,
1147
- // Reject & send back
1148
- reject_clarification,
1149
- // Override resolved validation
1150
- create_validation_clarification,
1151
- // Accept validation rules
1152
- accept_validation_rule,
1153
- accept_all_validation_rules,
1154
- // Hierarchical data model
1155
- form_data_entries,
1156
- update_form_data,
1157
- // Debug
1158
- show_debug_dialogs,
1159
- };
1160
- return (_jsx(FbFormContext.Provider, { value: ctx, children: _jsxs("div", { className: cn('cls_fb_form flex w-full', is_multi_instance ? 'h-full' : 'flex-col', props.className), children: [is_multi_instance && (_jsx(InstanceSidebar, { instances: instances, active_instance_id: active_instance_id, on_instance_change: set_active_instance, on_create: props.on_instance_create ? create_instance : undefined, user_role: props.user_role, instance_badge_render: props.instance_badge_render, className: props.sidebar_class_name })), _jsxs("div", { className: cn('flex-1 flex flex-col min-w-0 overflow-hidden', is_multi_instance && 'h-full'), children: [_jsxs("div", { className: cn('flex items-center justify-between border-b border-border/60 px-2 mb-4 flex-shrink-0', props.tab_bar_class_name), children: [_jsxs("div", { className: "flex gap-1 items-center", children: [_jsxs("button", { type: "button", onClick: () => state.set_active_tab('front'), className: cn('px-3 py-2 text-sm font-medium border-b-2 transition-colors', state.active_tab === 'front'
1161
- ? cn('border-foreground text-foreground', props.active_tab_class_name)
1162
- : 'border-transparent text-muted-foreground hover:text-foreground', props.tab_class_name), children: [props.front_tab_label ?? 'Client View', props.front_tab_badge && _jsx("span", { className: "ml-2", children: props.front_tab_badge })] }), props.agent_stepper && show_back_office ? (_jsx(AgentStepper, { steps: state.step_configs, active_step: state.active_step, on_step_click: (step) => {
1163
- // Confirm review decisions when navigating away from review step
1164
- if (state.active_step === 'review' && step !== 'review' && state.review_complete) {
1165
- const accepted_file_ids = confirm_review_decisions_with_data();
1166
- if (accepted_file_ids.length > 0) {
1167
- route_skipped_files(accepted_file_ids);
1168
- props.on_review_accepted?.(accepted_file_ids);
1169
- }
1170
- }
1171
- state.set_active_tab('agent_stepper');
1172
- state.set_active_step(step);
1173
- } })) : (_jsxs(_Fragment, { children: [show_back_office && (_jsx("button", { type: "button", onClick: () => state.set_active_tab('client_data'), className: cn('px-3 py-2 text-sm font-medium border-b-2 transition-colors', state.active_tab === 'client_data'
1174
- ? cn('border-foreground text-foreground', props.active_tab_class_name)
1175
- : 'border-transparent text-muted-foreground hover:text-foreground', props.tab_class_name), children: props.client_data_tab_label ?? 'Client Data' })), show_back_office && (_jsx("button", { type: "button", onClick: () => state.set_active_tab('back'), className: cn('px-3 py-2 text-sm font-medium border-b-2 transition-colors', state.active_tab === 'back'
1176
- ? cn('border-foreground text-foreground', props.active_tab_class_name)
1177
- : 'border-transparent text-muted-foreground hover:text-foreground', props.tab_class_name), children: props.back_tab_label ?? 'Back Office' })), show_back_office && (_jsxs("button", { type: "button", onClick: () => state.set_active_tab('clarifications'), className: cn('px-3 py-2 text-sm font-medium border-b-2 transition-colors relative', state.active_tab === 'clarifications'
1178
- ? cn('border-foreground text-foreground', props.active_tab_class_name)
1179
- : 'border-transparent text-muted-foreground hover:text-foreground', props.tab_class_name), children: [props.clarifications_tab_label ?? 'Send Back to Client', clarification_badge.total > 0 && (_jsx("span", { className: cn('ml-1.5 inline-flex items-center justify-center h-4 min-w-[16px] px-1 rounded text-[10px] font-semibold', clarification_badge.all_reviewed
1180
- ? 'bg-green-100 text-green-700'
1181
- : 'bg-amber-100 text-amber-700'), title: `Reviewed ${clarification_badge.reviewed} of ${clarification_badge.total} clarifications`, children: clarification_badge.total > 0
1182
- ? `${clarification_badge.reviewed} / ${clarification_badge.total}`
1183
- : clarification_badge.total }))] }))] }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [can_send_to_client && state.active_tab === 'front' && (_jsx("button", { type: "button", onClick: () => props.on_send_to_client(active_instance_id), disabled: !has_front_data, className: cn('inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium rounded-md transition-colors', has_front_data
1184
- ? 'bg-primary text-primary-foreground hover:bg-primary/90'
1185
- : 'bg-muted text-muted-foreground cursor-not-allowed opacity-50'), children: "Send to Client" })), is_past_instance && (_jsx("span", { className: "text-xs text-muted-foreground bg-muted px-2 py-1 rounded", children: "Read-only" }))] })] }), _jsxs("div", { className: cn('flex-1 overflow-y-auto px-4'), children: [_jsx("div", { style: { display: state.active_tab === 'front' ? undefined : 'none' }, children: _jsx(FrontOfficeView, {}) }), state.active_tab === 'client_data' && show_back_office && (_jsxs(_Fragment, { children: [show_backoffice_run_button && !is_past_instance && (_jsx("div", { className: "flex justify-end pt-2 mb-4", children: _jsx(BackofficeRunButton, { progress: state.run_progress, on_run: trigger_backoffice_run, disabled: !has_front_data, className: props.run_button_class_name, run_button_label: props.run_button_label, classification_results: state.classification_results, unassigned_files: state.unassigned_files, group_autofill_log: state.group_autofill_log }) })), _jsx(ClientDataView, {})] })), state.active_tab === 'back' && show_back_office && (_jsxs(_Fragment, { children: [show_backoffice_run_button && !is_past_instance && (_jsx("div", { className: "flex justify-end pt-2 mb-4", children: _jsx(BackofficeRunButton, { progress: state.run_progress, on_run: trigger_backoffice_run, disabled: !has_front_data, className: props.run_button_class_name, run_button_label: props.run_button_label, classification_results: state.classification_results, unassigned_files: state.unassigned_files, group_autofill_log: state.group_autofill_log }) })), _jsx(BackOfficeView, {})] })), state.active_tab === 'clarifications' && show_back_office && _jsx(ClarificationsView, {}), state.active_tab === 'agent_stepper' && show_back_office && (_jsxs(_Fragment, { children: [state.active_step === 'review' && _jsx(ReviewQueueView, {}), state.active_step === 'tax_data' && (_jsxs(_Fragment, { children: [show_backoffice_run_button && !is_past_instance && (_jsx("div", { className: "flex justify-end pt-2 mb-4", children: _jsx(BackofficeRunButton, { progress: state.run_progress, on_run: trigger_backoffice_run, disabled: !has_front_data && state.skipped_file_ids.size === 0 && state.classification_results.length === 0, className: props.run_button_class_name, run_button_label: props.run_button_label, classification_results: state.classification_results, unassigned_files: state.unassigned_files, group_autofill_log: state.group_autofill_log }) })), _jsx(FileStatusAccordion, {}), _jsx(BackOfficeView, {}), _jsxs("div", { className: "flex items-center justify-between border-t border-border/60 pt-4 mt-6 pb-4", children: [state.step_configs.find(s => s.id === 'review')?.enabled && (_jsx("button", { type: "button", onClick: () => state.set_active_step('review'), className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-muted-foreground hover:text-foreground transition-colors", children: "\u2190 Back to Review" })), state.send_back_items.length > 0 ? (_jsxs("button", { type: "button", onClick: () => state.set_active_step('send_back'), className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium bg-primary text-primary-foreground hover:bg-primary/90 rounded-md transition-colors", children: ["Next: Send Back (", state.send_back_items.length, ") \u2192"] })) : (_jsx("span", { className: "text-sm text-green-600 font-medium", children: "✓ All resolved" }))] })] })), state.active_step === 'send_back' && _jsx(SendBackView, {})] }))] })] }), _jsx(RunDetailsDialog, { open: details_open, on_open_change: set_details_open, progress: state.run_progress, classification_results: state.classification_results, unassigned_files: state.unassigned_files, group_autofill_log: state.group_autofill_log, show_run_log: show_debug_dialogs })] }) }));
1186
- }
1187
- HazoFbForm.displayName = 'HazoFbForm';
1188
- //# sourceMappingURL=hazo_fb_form.js.map