hazo_collab_forms 3.1.6 → 5.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 (284) hide show
  1. package/CHANGE_LOG.md +207 -0
  2. package/README.md +3 -0
  3. package/dist/components/clarification/clarification_item_body.d.ts +19 -1
  4. package/dist/components/clarification/clarification_item_body.d.ts.map +1 -1
  5. package/dist/components/clarification/clarification_item_body.js +114 -6
  6. package/dist/components/clarification/clarification_item_body.js.map +1 -1
  7. package/dist/components/clarification/clarification_thread.js +1 -1
  8. package/dist/components/clarification/clarification_thread.js.map +1 -1
  9. package/dist/components/clarification/index.d.ts +2 -0
  10. package/dist/components/clarification/index.d.ts.map +1 -1
  11. package/dist/components/clarification/index.js +1 -0
  12. package/dist/components/clarification/index.js.map +1 -1
  13. package/dist/components/clarification/resolution_status_strip.d.ts +18 -0
  14. package/dist/components/clarification/resolution_status_strip.d.ts.map +1 -0
  15. package/dist/components/clarification/resolution_status_strip.js +20 -0
  16. package/dist/components/clarification/resolution_status_strip.js.map +1 -0
  17. package/dist/components/hazo_fb_form/context.d.ts +1 -1
  18. package/dist/components/hazo_fb_form/context.d.ts.map +1 -1
  19. package/dist/components/hazo_fb_form/hazo_fb_form.d.ts.map +1 -1
  20. package/dist/components/hazo_fb_form/hazo_fb_form.js +330 -113
  21. package/dist/components/hazo_fb_form/hazo_fb_form.js.map +1 -1
  22. package/dist/components/hazo_fb_form/hooks/use_fb_form_state.d.ts +3 -3
  23. package/dist/components/hazo_fb_form/hooks/use_fb_form_state.d.ts.map +1 -1
  24. package/dist/components/hazo_fb_form/hooks/use_fb_form_state.js +340 -61
  25. package/dist/components/hazo_fb_form/hooks/use_fb_form_state.js.map +1 -1
  26. package/dist/components/hazo_fb_form/hooks/use_llm_run.d.ts +3 -1
  27. package/dist/components/hazo_fb_form/hooks/use_llm_run.d.ts.map +1 -1
  28. package/dist/components/hazo_fb_form/hooks/use_llm_run.js +89 -11
  29. package/dist/components/hazo_fb_form/hooks/use_llm_run.js.map +1 -1
  30. package/dist/components/hazo_fb_form/shared/agent_stepper.js +1 -1
  31. package/dist/components/hazo_fb_form/shared/agent_stepper.js.map +1 -1
  32. package/dist/components/hazo_fb_form/shared/file_status_accordion.d.ts +9 -0
  33. package/dist/components/hazo_fb_form/shared/file_status_accordion.d.ts.map +1 -0
  34. package/dist/components/hazo_fb_form/shared/file_status_accordion.js +39 -0
  35. package/dist/components/hazo_fb_form/shared/file_status_accordion.js.map +1 -0
  36. package/dist/components/hazo_fb_form/shared/format.d.ts.map +1 -1
  37. package/dist/components/hazo_fb_form/shared/format.js +8 -3
  38. package/dist/components/hazo_fb_form/shared/format.js.map +1 -1
  39. package/dist/components/hazo_fb_form/shared/send_back_item_card.d.ts +7 -1
  40. package/dist/components/hazo_fb_form/shared/send_back_item_card.d.ts.map +1 -1
  41. package/dist/components/hazo_fb_form/shared/send_back_item_card.js +6 -3
  42. package/dist/components/hazo_fb_form/shared/send_back_item_card.js.map +1 -1
  43. package/dist/components/hazo_fb_form/types.d.ts +3 -1
  44. package/dist/components/hazo_fb_form/types.d.ts.map +1 -1
  45. package/dist/components/hazo_fb_form/views/back_office_view.js +1 -1
  46. package/dist/components/hazo_fb_form/views/back_office_view.js.map +1 -1
  47. package/dist/components/hazo_fb_form/views/clarifications_view.js +2 -2
  48. package/dist/components/hazo_fb_form/views/clarifications_view.js.map +1 -1
  49. package/dist/components/hazo_fb_form/views/front_office_view.d.ts.map +1 -1
  50. package/dist/components/hazo_fb_form/views/front_office_view.js +62 -41
  51. package/dist/components/hazo_fb_form/views/front_office_view.js.map +1 -1
  52. package/dist/components/hazo_fb_form/views/interim_view.js +3 -3
  53. package/dist/components/hazo_fb_form/views/interim_view.js.map +1 -1
  54. package/dist/components/hazo_fb_form/views/review_queue_view.d.ts.map +1 -1
  55. package/dist/components/hazo_fb_form/views/review_queue_view.js +22 -9
  56. package/dist/components/hazo_fb_form/views/review_queue_view.js.map +1 -1
  57. package/dist/components/hazo_validation_rule_editor/components/rule_editor.d.ts.map +1 -1
  58. package/dist/components/hazo_validation_rule_editor/components/rule_editor.js +32 -3
  59. package/dist/components/hazo_validation_rule_editor/components/rule_editor.js.map +1 -1
  60. package/dist/components/hazo_validation_rule_editor/components/variable_chain_input.d.ts +20 -0
  61. package/dist/components/hazo_validation_rule_editor/components/variable_chain_input.d.ts.map +1 -0
  62. package/dist/components/hazo_validation_rule_editor/components/variable_chain_input.js +34 -0
  63. package/dist/components/hazo_validation_rule_editor/components/variable_chain_input.js.map +1 -0
  64. package/dist/components/hazo_validation_rule_editor/context.d.ts +3 -2
  65. package/dist/components/hazo_validation_rule_editor/context.d.ts.map +1 -1
  66. package/dist/components/hazo_validation_rule_editor/context.js +15 -3
  67. package/dist/components/hazo_validation_rule_editor/context.js.map +1 -1
  68. package/dist/components/hazo_validation_rule_editor/types.d.ts +7 -1
  69. package/dist/components/hazo_validation_rule_editor/types.d.ts.map +1 -1
  70. package/dist/components/hazo_validation_rule_editor/validation_rule_editor.d.ts +1 -1
  71. package/dist/components/hazo_validation_rule_editor/validation_rule_editor.d.ts.map +1 -1
  72. package/dist/components/hazo_validation_rule_editor/validation_rule_editor.js +2 -2
  73. package/dist/components/hazo_validation_rule_editor/validation_rule_editor.js.map +1 -1
  74. package/dist/components/index.d.ts +2 -0
  75. package/dist/components/index.d.ts.map +1 -1
  76. package/dist/components/index.js +2 -0
  77. package/dist/components/index.js.map +1 -1
  78. package/dist/components/shared/document_type_editor.d.ts +31 -0
  79. package/dist/components/shared/document_type_editor.d.ts.map +1 -0
  80. package/dist/components/shared/document_type_editor.js +60 -0
  81. package/dist/components/shared/document_type_editor.js.map +1 -0
  82. package/dist/components/shared/file_bar/file_bar.d.ts +7 -1
  83. package/dist/components/shared/file_bar/file_bar.d.ts.map +1 -1
  84. package/dist/components/shared/file_bar/file_bar.js +5 -3
  85. package/dist/components/shared/file_bar/file_bar.js.map +1 -1
  86. package/dist/components/shared/file_bar/file_bar_validation_dialog.js +4 -4
  87. package/dist/components/shared/file_bar/file_bar_validation_dialog.js.map +1 -1
  88. package/dist/components/shared/file_status_icon.d.ts +23 -0
  89. package/dist/components/shared/file_status_icon.d.ts.map +1 -0
  90. package/dist/components/shared/file_status_icon.js +38 -0
  91. package/dist/components/shared/file_status_icon.js.map +1 -0
  92. package/dist/components/shared/json_data_panel/json_data_panel.d.ts +1 -1
  93. package/dist/components/shared/json_data_panel/json_data_panel.d.ts.map +1 -1
  94. package/dist/components/shared/json_data_panel/json_data_panel.js +27 -2
  95. package/dist/components/shared/json_data_panel/json_data_panel.js.map +1 -1
  96. package/dist/components/shared/rule_result_card.d.ts.map +1 -1
  97. package/dist/components/shared/rule_result_card.js +5 -4
  98. package/dist/components/shared/rule_result_card.js.map +1 -1
  99. package/dist/components/thread_form/components/add_question_dialog.d.ts +12 -0
  100. package/dist/components/thread_form/components/add_question_dialog.d.ts.map +1 -0
  101. package/dist/components/thread_form/components/add_question_dialog.js +36 -0
  102. package/dist/components/thread_form/components/add_question_dialog.js.map +1 -0
  103. package/dist/components/thread_form/components/agent_compose_dialog.d.ts +30 -0
  104. package/dist/components/thread_form/components/agent_compose_dialog.d.ts.map +1 -0
  105. package/dist/components/thread_form/components/agent_compose_dialog.js +45 -0
  106. package/dist/components/thread_form/components/agent_compose_dialog.js.map +1 -0
  107. package/dist/components/thread_form/components/clarification.d.ts +14 -0
  108. package/dist/components/thread_form/components/clarification.d.ts.map +1 -0
  109. package/dist/components/thread_form/components/clarification.js +12 -0
  110. package/dist/components/thread_form/components/clarification.js.map +1 -0
  111. package/dist/components/thread_form/components/collected_data_view.d.ts +15 -0
  112. package/dist/components/thread_form/components/collected_data_view.d.ts.map +1 -0
  113. package/dist/components/thread_form/components/collected_data_view.js +121 -0
  114. package/dist/components/thread_form/components/collected_data_view.js.map +1 -0
  115. package/dist/components/thread_form/components/coverage_card.d.ts +11 -0
  116. package/dist/components/thread_form/components/coverage_card.d.ts.map +1 -0
  117. package/dist/components/thread_form/components/coverage_card.js +60 -0
  118. package/dist/components/thread_form/components/coverage_card.js.map +1 -0
  119. package/dist/components/thread_form/components/file_bar.d.ts +93 -0
  120. package/dist/components/thread_form/components/file_bar.d.ts.map +1 -0
  121. package/dist/components/thread_form/components/file_bar.js +251 -0
  122. package/dist/components/thread_form/components/file_bar.js.map +1 -0
  123. package/dist/components/thread_form/components/file_info_dialog.d.ts +15 -0
  124. package/dist/components/thread_form/components/file_info_dialog.d.ts.map +1 -0
  125. package/dist/components/thread_form/components/file_info_dialog.js +64 -0
  126. package/dist/components/thread_form/components/file_info_dialog.js.map +1 -0
  127. package/dist/components/thread_form/components/issue_group_tree.d.ts +20 -0
  128. package/dist/components/thread_form/components/issue_group_tree.d.ts.map +1 -0
  129. package/dist/components/thread_form/components/issue_group_tree.js +164 -0
  130. package/dist/components/thread_form/components/issue_group_tree.js.map +1 -0
  131. package/dist/components/thread_form/components/pdf_side_panel.d.ts +20 -0
  132. package/dist/components/thread_form/components/pdf_side_panel.d.ts.map +1 -0
  133. package/dist/components/thread_form/components/pdf_side_panel.js +63 -0
  134. package/dist/components/thread_form/components/pdf_side_panel.js.map +1 -0
  135. package/dist/components/thread_form/components/rule_decision_row.d.ts +31 -0
  136. package/dist/components/thread_form/components/rule_decision_row.d.ts.map +1 -0
  137. package/dist/components/thread_form/components/rule_decision_row.js +20 -0
  138. package/dist/components/thread_form/components/rule_decision_row.js.map +1 -0
  139. package/dist/components/thread_form/components/send_back_message.d.ts +32 -0
  140. package/dist/components/thread_form/components/send_back_message.d.ts.map +1 -0
  141. package/dist/components/thread_form/components/send_back_message.js +82 -0
  142. package/dist/components/thread_form/components/send_back_message.js.map +1 -0
  143. package/dist/components/thread_form/components/shared.d.ts +54 -0
  144. package/dist/components/thread_form/components/shared.d.ts.map +1 -0
  145. package/dist/components/thread_form/components/shared.js +136 -0
  146. package/dist/components/thread_form/components/shared.js.map +1 -0
  147. package/dist/components/thread_form/components/task_card.d.ts +90 -0
  148. package/dist/components/thread_form/components/task_card.d.ts.map +1 -0
  149. package/dist/components/thread_form/components/task_card.js +63 -0
  150. package/dist/components/thread_form/components/task_card.js.map +1 -0
  151. package/dist/components/thread_form/components/text_doc_check.d.ts +15 -0
  152. package/dist/components/thread_form/components/text_doc_check.d.ts.map +1 -0
  153. package/dist/components/thread_form/components/text_doc_check.js +16 -0
  154. package/dist/components/thread_form/components/text_doc_check.js.map +1 -0
  155. package/dist/components/thread_form/components/text_extraction.d.ts +14 -0
  156. package/dist/components/thread_form/components/text_extraction.d.ts.map +1 -0
  157. package/dist/components/thread_form/components/text_extraction.js +16 -0
  158. package/dist/components/thread_form/components/text_extraction.js.map +1 -0
  159. package/dist/components/thread_form/components/thread_composer.d.ts +15 -0
  160. package/dist/components/thread_form/components/thread_composer.d.ts.map +1 -0
  161. package/dist/components/thread_form/components/thread_composer.js +93 -0
  162. package/dist/components/thread_form/components/thread_composer.js.map +1 -0
  163. package/dist/components/thread_form/components/thread_timeline.d.ts +65 -0
  164. package/dist/components/thread_form/components/thread_timeline.d.ts.map +1 -0
  165. package/dist/components/thread_form/components/thread_timeline.js +225 -0
  166. package/dist/components/thread_form/components/thread_timeline.js.map +1 -0
  167. package/dist/components/thread_form/hooks/use_file_pipeline.d.ts +126 -0
  168. package/dist/components/thread_form/hooks/use_file_pipeline.d.ts.map +1 -0
  169. package/dist/components/thread_form/hooks/use_file_pipeline.js +760 -0
  170. package/dist/components/thread_form/hooks/use_file_pipeline.js.map +1 -0
  171. package/dist/components/thread_form/hooks/use_thread_form.d.ts +36 -0
  172. package/dist/components/thread_form/hooks/use_thread_form.d.ts.map +1 -0
  173. package/dist/components/thread_form/hooks/use_thread_form.js +126 -0
  174. package/dist/components/thread_form/hooks/use_thread_form.js.map +1 -0
  175. package/dist/components/thread_form/index.d.ts +33 -0
  176. package/dist/components/thread_form/index.d.ts.map +1 -0
  177. package/dist/components/thread_form/index.js +30 -0
  178. package/dist/components/thread_form/index.js.map +1 -0
  179. package/dist/components/thread_form/sample_data.d.ts +8 -0
  180. package/dist/components/thread_form/sample_data.d.ts.map +1 -0
  181. package/dist/components/thread_form/sample_data.js +658 -0
  182. package/dist/components/thread_form/sample_data.js.map +1 -0
  183. package/dist/components/thread_form/thread_form.d.ts +7 -0
  184. package/dist/components/thread_form/thread_form.d.ts.map +1 -0
  185. package/dist/components/thread_form/thread_form.js +1385 -0
  186. package/dist/components/thread_form/thread_form.js.map +1 -0
  187. package/dist/components/thread_form/types.d.ts +402 -0
  188. package/dist/components/thread_form/types.d.ts.map +1 -0
  189. package/dist/components/thread_form/types.js +23 -0
  190. package/dist/components/thread_form/types.js.map +1 -0
  191. package/dist/components/thread_form/utils/file_decision_state.d.ts +22 -0
  192. package/dist/components/thread_form/utils/file_decision_state.d.ts.map +1 -0
  193. package/dist/components/thread_form/utils/file_decision_state.js +37 -0
  194. package/dist/components/thread_form/utils/file_decision_state.js.map +1 -0
  195. package/dist/components/thread_form/utils/merge_send_back.d.ts +13 -0
  196. package/dist/components/thread_form/utils/merge_send_back.d.ts.map +1 -0
  197. package/dist/components/thread_form/utils/merge_send_back.js +23 -0
  198. package/dist/components/thread_form/utils/merge_send_back.js.map +1 -0
  199. package/dist/lib/autofill_handler.d.ts.map +1 -1
  200. package/dist/lib/autofill_handler.js +5 -44
  201. package/dist/lib/autofill_handler.js.map +1 -1
  202. package/dist/lib/classification_handler.d.ts +105 -0
  203. package/dist/lib/classification_handler.d.ts.map +1 -0
  204. package/dist/lib/classification_handler.js +342 -0
  205. package/dist/lib/classification_handler.js.map +1 -0
  206. package/dist/lib/content_gate_handler.d.ts +37 -0
  207. package/dist/lib/content_gate_handler.d.ts.map +1 -0
  208. package/dist/lib/content_gate_handler.js +126 -0
  209. package/dist/lib/content_gate_handler.js.map +1 -0
  210. package/dist/lib/index.d.ts +10 -0
  211. package/dist/lib/index.d.ts.map +1 -1
  212. package/dist/lib/index.js +5 -0
  213. package/dist/lib/index.js.map +1 -1
  214. package/dist/lib/periodic_coverage_runner.d.ts +24 -0
  215. package/dist/lib/periodic_coverage_runner.d.ts.map +1 -0
  216. package/dist/lib/periodic_coverage_runner.js +121 -0
  217. package/dist/lib/periodic_coverage_runner.js.map +1 -0
  218. package/dist/lib/resolution_handler.d.ts +150 -0
  219. package/dist/lib/resolution_handler.d.ts.map +1 -0
  220. package/dist/lib/resolution_handler.js +597 -0
  221. package/dist/lib/resolution_handler.js.map +1 -0
  222. package/dist/lib/resolve_variable.d.ts +25 -0
  223. package/dist/lib/resolve_variable.d.ts.map +1 -0
  224. package/dist/lib/resolve_variable.js +77 -0
  225. package/dist/lib/resolve_variable.js.map +1 -0
  226. package/dist/lib/validation_handler.d.ts +27 -3
  227. package/dist/lib/validation_handler.d.ts.map +1 -1
  228. package/dist/lib/validation_handler.js +338 -288
  229. package/dist/lib/validation_handler.js.map +1 -1
  230. package/dist/types/clarification.d.ts +54 -0
  231. package/dist/types/clarification.d.ts.map +1 -1
  232. package/dist/types/fb_form_data.d.ts +273 -123
  233. package/dist/types/fb_form_data.d.ts.map +1 -1
  234. package/dist/types/fb_form_data.js +44 -58
  235. package/dist/types/fb_form_data.js.map +1 -1
  236. package/dist/types/fb_form_data_v1.d.ts +250 -0
  237. package/dist/types/fb_form_data_v1.d.ts.map +1 -0
  238. package/dist/types/fb_form_data_v1.js +117 -0
  239. package/dist/types/fb_form_data_v1.js.map +1 -0
  240. package/dist/types/fb_form_instance.d.ts +1 -1
  241. package/dist/types/fb_form_instance.d.ts.map +1 -1
  242. package/dist/types/index.d.ts +5 -3
  243. package/dist/types/index.d.ts.map +1 -1
  244. package/dist/types/index.js +2 -1
  245. package/dist/types/index.js.map +1 -1
  246. package/dist/types/validation.d.ts +134 -12
  247. package/dist/types/validation.d.ts.map +1 -1
  248. package/dist/utils/expectation_extractor.d.ts +31 -0
  249. package/dist/utils/expectation_extractor.d.ts.map +1 -0
  250. package/dist/utils/expectation_extractor.js +142 -0
  251. package/dist/utils/expectation_extractor.js.map +1 -0
  252. package/dist/utils/fb_data_adapter.d.ts +7 -2
  253. package/dist/utils/fb_data_adapter.d.ts.map +1 -1
  254. package/dist/utils/fb_data_adapter.js +58 -7
  255. package/dist/utils/fb_data_adapter.js.map +1 -1
  256. package/dist/utils/fb_data_adapter_v2.d.ts +17 -0
  257. package/dist/utils/fb_data_adapter_v2.d.ts.map +1 -0
  258. package/dist/utils/fb_data_adapter_v2.js +483 -0
  259. package/dist/utils/fb_data_adapter_v2.js.map +1 -0
  260. package/dist/utils/fb_data_helpers.d.ts +1 -1
  261. package/dist/utils/fb_data_helpers.d.ts.map +1 -1
  262. package/dist/utils/fb_data_mutations.d.ts +1 -1
  263. package/dist/utils/fb_data_mutations.d.ts.map +1 -1
  264. package/dist/utils/fb_data_mutations_v2.d.ts +46 -0
  265. package/dist/utils/fb_data_mutations_v2.d.ts.map +1 -0
  266. package/dist/utils/fb_data_mutations_v2.js +341 -0
  267. package/dist/utils/fb_data_mutations_v2.js.map +1 -0
  268. package/dist/utils/fb_data_queries.d.ts +81 -0
  269. package/dist/utils/fb_data_queries.d.ts.map +1 -0
  270. package/dist/utils/fb_data_queries.js +354 -0
  271. package/dist/utils/fb_data_queries.js.map +1 -0
  272. package/dist/utils/index.d.ts +4 -0
  273. package/dist/utils/index.d.ts.map +1 -1
  274. package/dist/utils/index.js +6 -0
  275. package/dist/utils/index.js.map +1 -1
  276. package/dist/utils/issue_bucketing.d.ts +36 -0
  277. package/dist/utils/issue_bucketing.d.ts.map +1 -0
  278. package/dist/utils/issue_bucketing.js +107 -0
  279. package/dist/utils/issue_bucketing.js.map +1 -0
  280. package/dist/utils/validation_result.d.ts +32 -0
  281. package/dist/utils/validation_result.d.ts.map +1 -0
  282. package/dist/utils/validation_result.js +55 -0
  283. package/dist/utils/validation_result.js.map +1 -0
  284. package/package.json +16 -4
@@ -11,6 +11,8 @@ import { cn } from '../../utils/cn.js';
11
11
  import { use_logger } from '../../logger/index.js';
12
12
  import { is_fb_terminal_status } from '../../types/fb_form_instance.js';
13
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';
14
16
  import { FbFormContext } from './context.js';
15
17
  import { use_fb_form_state } from './hooks/use_fb_form_state.js';
16
18
  import { use_llm_run } from './hooks/use_llm_run.js';
@@ -25,6 +27,7 @@ import { register_ihelp_icon } from '../shared/ihelp_icon.js';
25
27
  import { AgentStepper } from './shared/agent_stepper.js';
26
28
  import { ReviewQueueView } from './views/review_queue_view.js';
27
29
  import { SendBackView } from './views/send_back_view.js';
30
+ import { FileStatusAccordion } from './shared/file_status_accordion.js';
28
31
  export const VIRTUAL_INSTANCE_ID = '__single__';
29
32
  export function HazoFbForm(props) {
30
33
  const logger = use_logger();
@@ -93,7 +96,7 @@ export function HazoFbForm(props) {
93
96
  });
94
97
  }
95
98
  }, [props.data, props.on_data_change]);
96
- const state = use_fb_form_state(effective_props, active_instance_id, active_instance, pending_clarification_responses);
99
+ const state = use_fb_form_state(effective_props, active_instance_id, active_instance, pending_clarification_responses, form_data_entries);
97
100
  const { trigger_run, trigger_complete, trigger_classify_file, trigger_assign_file, route_skipped_files, trigger_backoffice_run } = use_llm_run({
98
101
  props: effective_props,
99
102
  update_progress: state.update_progress,
@@ -116,6 +119,7 @@ export function HazoFbForm(props) {
116
119
  set_validating_file_ids: state.set_validating_file_ids,
117
120
  skipped_file_ids: state.skipped_file_ids,
118
121
  update_form_data,
122
+ form_data_entries,
119
123
  });
120
124
  // Restore group_autofill_log from persisted autofill activities in the data model.
121
125
  // This runs on mount (and when form_data_entries changes) so that after a page refresh,
@@ -127,7 +131,7 @@ export function HazoFbForm(props) {
127
131
  if (form_data_entries.length === 0)
128
132
  return;
129
133
  const autofill_activities = [];
130
- // Walk all entries looking for autofill activities
134
+ // Walk all entries looking for autofill activities (v1 structure during migration)
131
135
  for (const entry of form_data_entries) {
132
136
  const walk = (items) => {
133
137
  if (!items)
@@ -150,8 +154,10 @@ export function HazoFbForm(props) {
150
154
  }
151
155
  };
152
156
  walk(entry.client_data);
153
- if (entry.clarification?.client_data)
154
- walk(entry.clarification.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);
155
161
  }
156
162
  if (autofill_activities.length > 0) {
157
163
  autofill_restore_ran.current = true;
@@ -193,102 +199,174 @@ export function HazoFbForm(props) {
193
199
  state.set_group_autofill_log(restored_log);
194
200
  }
195
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
196
272
  // Sync flat LLM results → hierarchical data model
197
273
  // Rebuild data[] whenever classification, validation, or clarification state changes.
198
274
  // Combines ALL instances into a single data[] array (matching the reference JSON structure).
199
275
  // IMPORTANT: The JSON must be stable regardless of which instance is active — every instance
200
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.
201
289
  useEffect(() => {
202
- if (!active_instance)
290
+ if (instances.length === 0)
203
291
  return;
204
- // Enrichment helpers that apply live state (only relevant for the active instance)
205
- const sent_map = new Map(state.sent_clarifications.map(s => [s.id, s]));
206
- const enrich = (item) => {
207
- let result = item;
208
- const sent = sent_map.get(item.id);
209
- if (sent?.response_choice || sent?.user_comment) {
210
- result = sent;
211
- }
212
- else {
213
- const pending = pending_clarification_responses.get(item.id);
214
- if (pending?.response_choice || pending?.user_comment) {
215
- result = {
216
- ...item,
217
- status: 'responded',
218
- response_choice: pending.response_choice,
219
- user_comment: pending.user_comment,
220
- response_files: pending.response_files ?? [],
221
- };
222
- }
223
- }
224
- const decision = state.review_queue_decisions.get(item.id);
225
- if (decision) {
226
- result = { ...result, review_decision: decision === 'accept' ? 'approve' : 'skip_process' };
227
- }
228
- const comment = state.send_back_comments.get(item.id);
229
- if (comment) {
230
- result = { ...result, agent_feedback: comment };
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;
231
298
  }
232
- return result;
233
- };
234
- /** Collect all clarifications for an instance from its persisted sources */
235
- const collect_instance_clarifications = (inst) => {
236
- const front_clrs = Array.isArray(inst.front_form_data?.['__clarifications'])
237
- ? inst.front_form_data['__clarifications']
238
- : [];
239
- const clrs = [...(inst.sent_clarifications ?? [])];
240
- const seen = new Set(clrs.map(c => c.id));
241
- for (const c of front_clrs) {
242
- if (!seen.has(c.id)) {
243
- seen.add(c.id);
244
- clrs.push(c);
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);
245
304
  }
246
305
  }
247
- return clrs;
248
- };
249
- // Build data for ALL instances
250
- const all_data = [];
251
- for (let inst_idx = 0; inst_idx < instances.length; inst_idx++) {
252
- const inst = instances[inst_idx];
253
- const inst_num = inst_idx + 1;
254
- const is_active = inst.instance_id === active_instance_id;
255
- // Start with persisted clarifications from all sources
256
- let inst_clrs = collect_instance_clarifications(inst);
257
- if (is_active) {
258
- // Active instance: merge in live draft clarifications and apply enrichments
259
- const draft_ids = new Set(state.draft_clarifications.map(d => d.id));
260
- const persisted_ids = new Set(inst_clrs.map(c => c.id));
261
- // Add any draft clarifications not already in persisted data
262
- for (const d of state.draft_clarifications) {
263
- if (!persisted_ids.has(d.id))
264
- inst_clrs.push(d);
265
- }
266
- // Replace persisted versions with enriched ones
267
- inst_clrs = inst_clrs.map(c => {
268
- // If in drafts, use draft version (may have updated response_options)
269
- const draft = draft_ids.has(c.id) ? state.draft_clarifications.find(d => d.id === c.id) : undefined;
270
- return enrich(draft ?? c);
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;
271
358
  });
272
- }
273
- const synthetic = {
274
- ...inst,
275
- ...(is_active ? {
276
- classification_results: state.classification_results.length > 0 ? state.classification_results : inst.classification_results,
277
- file_validation_results: Object.keys(state.file_validation_results).length > 0 ? state.file_validation_results : inst.file_validation_results,
278
- } : {}),
279
- sent_clarifications: inst_clrs.length > 0 ? inst_clrs : inst.sent_clarifications,
280
- };
281
- const has_data = (synthetic.classification_results?.length ?? 0) > 0
282
- || Object.keys(synthetic.file_validation_results ?? {}).length > 0
283
- || (synthetic.sent_clarifications?.length ?? 0) > 0;
284
- if (has_data) {
285
- all_data.push(...flat_to_data(synthetic, inst_num));
286
- }
287
- }
288
- if (all_data.length > 0) {
289
- update_form_data(() => all_data);
290
- }
291
- }, [state.classification_results, state.file_validation_results, state.sent_clarifications, state.draft_clarifications, pending_clarification_responses, state.review_queue_decisions, state.send_back_comments, instances, active_instance_id]);
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]);
292
370
  // Persist classification_results to instance when they change
293
371
  const prev_cls_ref = useRef(state.classification_results);
294
372
  useEffect(() => {
@@ -320,9 +398,15 @@ export function HazoFbForm(props) {
320
398
  let changed = false;
321
399
  const updated = current_sent.map(c => {
322
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;
323
405
  const decision = state.review_queue_decisions.get(c.id);
324
- if (decision && result.review_decision !== (decision === 'accept' ? 'approve' : 'skip_process')) {
325
- result = { ...result, review_decision: decision === 'accept' ? 'approve' : 'skip_process' };
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 };
326
410
  changed = true;
327
411
  }
328
412
  const comment = state.send_back_comments.get(c.id);
@@ -426,8 +510,23 @@ export function HazoFbForm(props) {
426
510
  // Upsert: if item wasn't in sent_clarifications (e.g. validation draft), add it
427
511
  if (!found) {
428
512
  const draft = state.draft_clarifications.find((d) => d.id === id);
429
- if (draft)
513
+ if (draft) {
430
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
+ }
431
530
  }
432
531
  // Also update front_form_data.__clarifications if the item lives there
433
532
  const front_clrs = active_instance.front_form_data?.['__clarifications'];
@@ -438,6 +537,11 @@ export function HazoFbForm(props) {
438
537
  __clarifications: front_clrs.map(c => c.id === id ? { ...c, ...updates, updated_at: now } : c),
439
538
  };
440
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
+ });
441
545
  props.on_instance_update?.(active_instance_id, inst_updates);
442
546
  }
443
547
  else {
@@ -455,34 +559,122 @@ export function HazoFbForm(props) {
455
559
  });
456
560
  if (!found) {
457
561
  const draft = state.draft_clarifications.find((d) => d.id === id);
458
- if (draft)
562
+ if (draft) {
459
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
+ }
460
577
  }
461
578
  props.on_back_change?.('__clarifications', updated);
462
579
  }
463
- }, [props, is_multi_instance, active_instance, active_instance_id, state.draft_clarifications]);
580
+ }, [props, is_multi_instance, active_instance, active_instance_id, state.draft_clarifications, state.file_validation_results]);
464
581
  const update_clarification_response = useCallback((id, response) => {
465
- // If the clarification has a thread (resent item), append the client's new response to it
466
- const all_items = [
467
- ...(is_multi_instance ? (active_instance?.sent_clarifications ?? []) : []),
468
- ...(Array.isArray(props.back_form_data?.['__clarifications'])
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'])
469
601
  ? props.back_form_data['__clarifications']
470
- : []),
471
- ];
472
- const existing_item = all_items.find(c => c.id === id);
473
- const has_thread = existing_item?.thread && existing_item.thread.length > 0;
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];
474
634
  const updates = {
475
635
  status: 'responded',
476
636
  response_choice: response.response_choice,
477
637
  user_comment: response.user_comment,
478
638
  response_files: response.response_files ?? [],
639
+ updated_at: now,
640
+ thread: updated_thread,
479
641
  };
480
- // Do NOT append client response to thread history here — the thread captures
481
- // completed conversation rounds (issue → client response → agent rejection).
482
- // The current response is displayed in the response form section, and will be
483
- // added to the thread when the agent reviews and sends back again.
484
- update_clarification_status(id, updates);
485
- }, [update_clarification_status, is_multi_instance, active_instance, props.back_form_data]);
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]);
486
678
  /** Batch-update multiple responses in a single write to avoid stale-state overwrites */
487
679
  const batch_update_clarification_responses = useCallback((responses) => {
488
680
  if (responses.size === 0)
@@ -716,7 +908,7 @@ export function HazoFbForm(props) {
716
908
  rule_id: rule.rule_id,
717
909
  rule_name: rule.rule_name,
718
910
  issue_description: message,
719
- validation_details: rule.issue_description ?? rule.raw_response ?? '',
911
+ validation_details: rule.summary ?? rule.issues[0]?.issue_description ?? rule.raw_response ?? '',
720
912
  doc_references: [{
721
913
  file_id: file.file_id,
722
914
  file_name: file.file_name,
@@ -830,8 +1022,14 @@ export function HazoFbForm(props) {
830
1022
  if (!data)
831
1023
  return false;
832
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
+ }
833
1031
  if (key.startsWith('__'))
834
- return false; // Skip internal keys
1032
+ return false; // Skip other internal keys
835
1033
  if (val === undefined || val === null || val === '')
836
1034
  return false;
837
1035
  if (Array.isArray(val) && val.length === 0)
@@ -841,6 +1039,25 @@ export function HazoFbForm(props) {
841
1039
  }, [effective_props.front_form_data]);
842
1040
  // Send to Client button: show when callback is provided, instance is not past/terminal, and on front tab
843
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]);
844
1061
  const ctx = {
845
1062
  props: effective_props,
846
1063
  active_tab: state.active_tab,
@@ -926,7 +1143,7 @@ export function HazoFbForm(props) {
926
1143
  send_back_items: state.send_back_items,
927
1144
  send_back_comments: state.send_back_comments,
928
1145
  set_send_back_comments: state.set_send_back_comments,
929
- confirm_review_decisions: state.confirm_review_decisions,
1146
+ confirm_review_decisions: confirm_review_decisions_with_data,
930
1147
  // Reject & send back
931
1148
  reject_clarification,
932
1149
  // Override resolved validation
@@ -945,7 +1162,7 @@ export function HazoFbForm(props) {
945
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) => {
946
1163
  // Confirm review decisions when navigating away from review step
947
1164
  if (state.active_step === 'review' && step !== 'review' && state.review_complete) {
948
- const accepted_file_ids = state.confirm_review_decisions();
1165
+ const accepted_file_ids = confirm_review_decisions_with_data();
949
1166
  if (accepted_file_ids.length > 0) {
950
1167
  route_skipped_files(accepted_file_ids);
951
1168
  props.on_review_accepted?.(accepted_file_ids);
@@ -965,7 +1182,7 @@ export function HazoFbForm(props) {
965
1182
  ? `${clarification_badge.reviewed} / ${clarification_badge.total}`
966
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
967
1184
  ? 'bg-primary text-primary-foreground hover:bg-primary/90'
968
- : '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, 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, {}), _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: "&check; 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 })] }) }));
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: "&check; 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 })] }) }));
969
1186
  }
970
1187
  HazoFbForm.displayName = 'HazoFbForm';
971
1188
  //# sourceMappingURL=hazo_fb_form.js.map