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.
- package/CHANGE_LOG.md +115 -0
- package/README.md +203 -111
- package/dist/audit/built_in_actions.d.ts +6 -0
- package/dist/audit/built_in_actions.d.ts.map +1 -0
- package/dist/audit/built_in_actions.js +23 -0
- package/dist/audit/built_in_actions.js.map +1 -0
- package/dist/audit/intent_emitter.d.ts +4 -0
- package/dist/audit/intent_emitter.d.ts.map +1 -0
- package/dist/audit/intent_emitter.js +25 -0
- package/dist/audit/intent_emitter.js.map +1 -0
- package/dist/audit/lazy_audit_icon.d.ts +8 -0
- package/dist/audit/lazy_audit_icon.d.ts.map +1 -0
- package/dist/audit/lazy_audit_icon.js +20 -0
- package/dist/audit/lazy_audit_icon.js.map +1 -0
- package/dist/components/_internal_form_set.d.ts +6 -0
- package/dist/components/_internal_form_set.d.ts.map +1 -1
- package/dist/components/_internal_form_set.js +48 -51
- package/dist/components/_internal_form_set.js.map +1 -1
- package/dist/components/clarification/clarification_item_body.d.ts +1 -1
- package/dist/components/clarification/clarification_item_body.d.ts.map +1 -1
- package/dist/components/clarification/resolution_status_strip.d.ts +1 -1
- package/dist/components/clarification/resolution_status_strip.d.ts.map +1 -1
- package/dist/components/field_audit/auditor.d.ts +30 -0
- package/dist/components/field_audit/auditor.d.ts.map +1 -0
- package/dist/components/field_audit/auditor.js +91 -0
- package/dist/components/field_audit/auditor.js.map +1 -0
- package/dist/components/field_audit/context.d.ts +29 -0
- package/dist/components/field_audit/context.d.ts.map +1 -0
- package/dist/components/field_audit/context.js +123 -0
- package/dist/components/field_audit/context.js.map +1 -0
- package/dist/components/field_audit/field_audit_icon.d.ts +12 -0
- package/dist/components/field_audit/field_audit_icon.d.ts.map +1 -0
- package/dist/components/field_audit/field_audit_icon.js +23 -0
- package/dist/components/field_audit/field_audit_icon.js.map +1 -0
- package/dist/components/field_audit/field_audit_panel.d.ts +9 -0
- package/dist/components/field_audit/field_audit_panel.d.ts.map +1 -0
- package/dist/components/field_audit/field_audit_panel.js +54 -0
- package/dist/components/field_audit/field_audit_panel.js.map +1 -0
- package/dist/components/field_audit/index.d.ts +33 -0
- package/dist/components/field_audit/index.d.ts.map +1 -0
- package/dist/components/field_audit/index.js +29 -0
- package/dist/components/field_audit/index.js.map +1 -0
- package/dist/components/field_audit/types.d.ts +75 -0
- package/dist/components/field_audit/types.d.ts.map +1 -0
- package/dist/components/field_audit/types.js +10 -0
- package/dist/components/field_audit/types.js.map +1 -0
- package/dist/components/field_audit/with_field_audit.d.ts +32 -0
- package/dist/components/field_audit/with_field_audit.d.ts.map +1 -0
- package/dist/components/field_audit/with_field_audit.js +42 -0
- package/dist/components/field_audit/with_field_audit.js.map +1 -0
- package/dist/components/hazo_collab_form_checkbox.d.ts.map +1 -1
- package/dist/components/hazo_collab_form_checkbox.js +3 -1
- package/dist/components/hazo_collab_form_checkbox.js.map +1 -1
- package/dist/components/hazo_collab_form_doc.d.ts.map +1 -1
- package/dist/components/hazo_collab_form_doc.js +4 -1
- package/dist/components/hazo_collab_form_doc.js.map +1 -1
- package/dist/components/hazo_collab_form_radio.d.ts.map +1 -1
- package/dist/components/hazo_collab_form_radio.js +4 -2
- package/dist/components/hazo_collab_form_radio.js.map +1 -1
- package/dist/components/hazo_collab_form_view/context.d.ts +7 -0
- package/dist/components/hazo_collab_form_view/context.d.ts.map +1 -1
- package/dist/components/hazo_collab_form_view/context.js +46 -0
- package/dist/components/hazo_collab_form_view/context.js.map +1 -1
- package/dist/components/hazo_collab_form_view/hooks/use_view_callbacks.d.ts +8 -1
- package/dist/components/hazo_collab_form_view/hooks/use_view_callbacks.d.ts.map +1 -1
- package/dist/components/hazo_collab_form_view/hooks/use_view_callbacks.js +4 -2
- package/dist/components/hazo_collab_form_view/hooks/use_view_callbacks.js.map +1 -1
- package/dist/components/hazo_collab_form_view/index.d.ts +1 -1
- package/dist/components/hazo_collab_form_view/index.d.ts.map +1 -1
- package/dist/components/hazo_collab_form_view/index.js +59 -3
- package/dist/components/hazo_collab_form_view/index.js.map +1 -1
- package/dist/components/hazo_collab_form_view/types.d.ts +134 -0
- package/dist/components/hazo_collab_form_view/types.d.ts.map +1 -1
- package/dist/components/hazo_collab_form_view/views/approval_view.d.ts.map +1 -1
- package/dist/components/hazo_collab_form_view/views/approval_view.js +3 -1
- package/dist/components/hazo_collab_form_view/views/approval_view.js.map +1 -1
- package/dist/components/hazo_collab_form_view/views/edit_view.d.ts.map +1 -1
- package/dist/components/hazo_collab_form_view/views/edit_view.js +8 -3
- package/dist/components/hazo_collab_form_view/views/edit_view.js.map +1 -1
- package/dist/components/hazo_collab_form_view/views/print_view.d.ts.map +1 -1
- package/dist/components/hazo_collab_form_view/views/print_view.js +3 -1
- package/dist/components/hazo_collab_form_view/views/print_view.js.map +1 -1
- package/dist/components/hazo_collab_form_view/views/summary_view.d.ts.map +1 -1
- package/dist/components/hazo_collab_form_view/views/summary_view.js +4 -2
- package/dist/components/hazo_collab_form_view/views/summary_view.js.map +1 -1
- package/dist/components/hazo_data_form/group_renderer.d.ts +8 -2
- package/dist/components/hazo_data_form/group_renderer.d.ts.map +1 -1
- package/dist/components/hazo_data_form/group_renderer.js +3 -3
- package/dist/components/hazo_data_form/group_renderer.js.map +1 -1
- package/dist/components/hazo_data_form/hazo_data_form.d.ts +2 -1
- package/dist/components/hazo_data_form/hazo_data_form.d.ts.map +1 -1
- package/dist/components/hazo_data_form/hazo_data_form.js +47 -6
- package/dist/components/hazo_data_form/hazo_data_form.js.map +1 -1
- package/dist/components/hazo_data_form/section_renderer.d.ts +4 -2
- package/dist/components/hazo_data_form/section_renderer.d.ts.map +1 -1
- package/dist/components/hazo_data_form/section_renderer.js +2 -2
- package/dist/components/hazo_data_form/section_renderer.js.map +1 -1
- package/dist/components/hazo_data_form/shared/data_form_field_layout.d.ts +4 -1
- package/dist/components/hazo_data_form/shared/data_form_field_layout.d.ts.map +1 -1
- package/dist/components/hazo_data_form/shared/data_form_field_layout.js +34 -8
- package/dist/components/hazo_data_form/shared/data_form_field_layout.js.map +1 -1
- package/dist/components/hazo_data_form/types.d.ts +56 -1
- package/dist/components/hazo_data_form/types.d.ts.map +1 -1
- package/dist/components/index.d.ts +5 -5
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +4 -4
- package/dist/components/index.js.map +1 -1
- package/dist/components/shared/base_field_layout.d.ts.map +1 -1
- package/dist/components/shared/base_field_layout.js +5 -1
- package/dist/components/shared/base_field_layout.js.map +1 -1
- package/dist/components/shared/field_action_array_slot.d.ts +10 -0
- package/dist/components/shared/field_action_array_slot.d.ts.map +1 -0
- package/dist/components/shared/field_action_array_slot.js +33 -0
- package/dist/components/shared/field_action_array_slot.js.map +1 -0
- package/dist/components/shared/field_action_slot.d.ts +22 -0
- package/dist/components/shared/field_action_slot.d.ts.map +1 -0
- package/dist/components/shared/field_action_slot.js +20 -0
- package/dist/components/shared/field_action_slot.js.map +1 -0
- package/dist/components/shared/ihelp_icon.d.ts +1 -1
- package/dist/components/shared/ihelp_icon.js +1 -1
- package/dist/components/shared/rule_result_card.js +1 -1
- package/dist/components/shared/rule_result_card.js.map +1 -1
- package/dist/components/shared/use_field_action_slot.d.ts +37 -0
- package/dist/components/shared/use_field_action_slot.d.ts.map +1 -0
- package/dist/components/shared/use_field_action_slot.js +77 -0
- package/dist/components/shared/use_field_action_slot.js.map +1 -0
- package/dist/components/thread_form/components/key_info_drawer.d.ts +7 -1
- package/dist/components/thread_form/components/key_info_drawer.d.ts.map +1 -1
- package/dist/components/thread_form/components/key_info_drawer.js +2 -2
- package/dist/components/thread_form/components/key_info_drawer.js.map +1 -1
- package/dist/components/thread_form/components/send_back_message.d.ts +1 -1
- package/dist/components/thread_form/components/send_back_message.d.ts.map +1 -1
- package/dist/components/thread_form/hooks/use_file_pipeline.d.ts +1 -1
- package/dist/components/thread_form/hooks/use_file_pipeline.d.ts.map +1 -1
- package/dist/components/thread_form/hooks/use_file_pipeline.js +1 -1
- package/dist/components/thread_form/hooks/use_file_pipeline.js.map +1 -1
- package/dist/components/thread_form/index.d.ts +1 -1
- package/dist/components/thread_form/index.d.ts.map +1 -1
- package/dist/components/thread_form/index.js.map +1 -1
- package/dist/components/thread_form/thread_form.d.ts.map +1 -1
- package/dist/components/thread_form/thread_form.js +3 -3
- package/dist/components/thread_form/thread_form.js.map +1 -1
- package/dist/components/thread_form/types.d.ts +32 -4
- package/dist/components/thread_form/types.d.ts.map +1 -1
- package/dist/components/thread_form/types.js.map +1 -1
- package/dist/lib/index.d.ts +0 -2
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +0 -2
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/resolution_handler.d.ts +1 -1
- package/dist/lib/resolution_handler.d.ts.map +1 -1
- package/dist/lib/resolve_variable.d.ts +1 -1
- package/dist/lib/resolve_variable.d.ts.map +1 -1
- package/dist/types/clarification.d.ts +1 -1
- package/dist/types/clarification.d.ts.map +1 -1
- package/dist/types/field_action.d.ts +25 -0
- package/dist/types/field_action.d.ts.map +1 -0
- package/dist/types/field_action.js +8 -0
- package/dist/types/field_action.js.map +1 -0
- package/dist/types/index.d.ts +3 -6
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -3
- package/dist/types/index.js.map +1 -1
- package/dist/types/{fb_form_data.d.ts → shared_data.d.ts} +1 -3
- package/dist/types/shared_data.d.ts.map +1 -0
- package/dist/types/{fb_form_data.js → shared_data.js} +1 -2
- package/dist/types/shared_data.js.map +1 -0
- package/dist/utils/dev_file_manager.d.ts +1 -1
- package/dist/utils/dev_file_manager.js +1 -1
- package/dist/{components/hazo_fb_form/shared → utils}/format.d.ts +2 -2
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/{components/hazo_fb_form/shared → utils}/format.js +1 -1
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/index.d.ts +1 -9
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -15
- package/dist/utils/index.js.map +1 -1
- package/package.json +6 -1
- package/dist/components/hazo_fb_form/components/backoffice_run_button.d.ts +0 -18
- package/dist/components/hazo_fb_form/components/backoffice_run_button.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/components/backoffice_run_button.js +0 -23
- package/dist/components/hazo_fb_form/components/backoffice_run_button.js.map +0 -1
- package/dist/components/hazo_fb_form/components/draft_clarification_card.d.ts +0 -39
- package/dist/components/hazo_fb_form/components/draft_clarification_card.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/components/draft_clarification_card.js +0 -94
- package/dist/components/hazo_fb_form/components/draft_clarification_card.js.map +0 -1
- package/dist/components/hazo_fb_form/components/fb_document_type_editor.d.ts +0 -11
- package/dist/components/hazo_fb_form/components/fb_document_type_editor.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/components/fb_document_type_editor.js +0 -82
- package/dist/components/hazo_fb_form/components/fb_document_type_editor.js.map +0 -1
- package/dist/components/hazo_fb_form/components/fb_tag_editor.d.ts +0 -11
- package/dist/components/hazo_fb_form/components/fb_tag_editor.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/components/fb_tag_editor.js +0 -107
- package/dist/components/hazo_fb_form/components/fb_tag_editor.js.map +0 -1
- package/dist/components/hazo_fb_form/components/front_office_stepper.d.ts +0 -15
- package/dist/components/hazo_fb_form/components/front_office_stepper.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/components/front_office_stepper.js +0 -21
- package/dist/components/hazo_fb_form/components/front_office_stepper.js.map +0 -1
- package/dist/components/hazo_fb_form/components/instance_sidebar.d.ts +0 -21
- package/dist/components/hazo_fb_form/components/instance_sidebar.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/components/instance_sidebar.js +0 -58
- package/dist/components/hazo_fb_form/components/instance_sidebar.js.map +0 -1
- package/dist/components/hazo_fb_form/components/reject_clarification_dialog.d.ts +0 -15
- package/dist/components/hazo_fb_form/components/reject_clarification_dialog.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/components/reject_clarification_dialog.js +0 -26
- package/dist/components/hazo_fb_form/components/reject_clarification_dialog.js.map +0 -1
- package/dist/components/hazo_fb_form/components/run_button.d.ts +0 -19
- package/dist/components/hazo_fb_form/components/run_button.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/components/run_button.js +0 -38
- package/dist/components/hazo_fb_form/components/run_button.js.map +0 -1
- package/dist/components/hazo_fb_form/components/run_details_dialog.d.ts +0 -17
- package/dist/components/hazo_fb_form/components/run_details_dialog.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/components/run_details_dialog.js +0 -35
- package/dist/components/hazo_fb_form/components/run_details_dialog.js.map +0 -1
- package/dist/components/hazo_fb_form/components/sent_clarification_group.d.ts +0 -30
- package/dist/components/hazo_fb_form/components/sent_clarification_group.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/components/sent_clarification_group.js +0 -76
- package/dist/components/hazo_fb_form/components/sent_clarification_group.js.map +0 -1
- package/dist/components/hazo_fb_form/components/tag_pill.d.ts +0 -15
- package/dist/components/hazo_fb_form/components/tag_pill.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/components/tag_pill.js +0 -15
- package/dist/components/hazo_fb_form/components/tag_pill.js.map +0 -1
- package/dist/components/hazo_fb_form/context.d.ts +0 -135
- package/dist/components/hazo_fb_form/context.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/context.js +0 -13
- package/dist/components/hazo_fb_form/context.js.map +0 -1
- package/dist/components/hazo_fb_form/hazo_fb_form.d.ts +0 -13
- package/dist/components/hazo_fb_form/hazo_fb_form.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/hazo_fb_form.js +0 -1188
- package/dist/components/hazo_fb_form/hazo_fb_form.js.map +0 -1
- package/dist/components/hazo_fb_form/hooks/use_fb_form_state.d.ts +0 -58
- package/dist/components/hazo_fb_form/hooks/use_fb_form_state.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/hooks/use_fb_form_state.js +0 -919
- package/dist/components/hazo_fb_form/hooks/use_fb_form_state.js.map +0 -1
- package/dist/components/hazo_fb_form/hooks/use_llm_run.d.ts +0 -52
- package/dist/components/hazo_fb_form/hooks/use_llm_run.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/hooks/use_llm_run.js +0 -1863
- package/dist/components/hazo_fb_form/hooks/use_llm_run.js.map +0 -1
- package/dist/components/hazo_fb_form/index.d.ts +0 -29
- package/dist/components/hazo_fb_form/index.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/index.js +0 -19
- package/dist/components/hazo_fb_form/index.js.map +0 -1
- package/dist/components/hazo_fb_form/shared/agent_stepper.d.ts +0 -9
- package/dist/components/hazo_fb_form/shared/agent_stepper.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/shared/agent_stepper.js +0 -17
- package/dist/components/hazo_fb_form/shared/agent_stepper.js.map +0 -1
- package/dist/components/hazo_fb_form/shared/clarification_helpers.d.ts +0 -15
- package/dist/components/hazo_fb_form/shared/clarification_helpers.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/shared/clarification_helpers.js +0 -23
- package/dist/components/hazo_fb_form/shared/clarification_helpers.js.map +0 -1
- package/dist/components/hazo_fb_form/shared/file_status_accordion.d.ts +0 -9
- package/dist/components/hazo_fb_form/shared/file_status_accordion.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/shared/file_status_accordion.js +0 -39
- package/dist/components/hazo_fb_form/shared/file_status_accordion.js.map +0 -1
- package/dist/components/hazo_fb_form/shared/file_utils.d.ts +0 -9
- package/dist/components/hazo_fb_form/shared/file_utils.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/shared/file_utils.js +0 -31
- package/dist/components/hazo_fb_form/shared/file_utils.js.map +0 -1
- package/dist/components/hazo_fb_form/shared/format.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/shared/format.js.map +0 -1
- package/dist/components/hazo_fb_form/shared/group_debug_icon.d.ts +0 -15
- package/dist/components/hazo_fb_form/shared/group_debug_icon.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/shared/group_debug_icon.js +0 -48
- package/dist/components/hazo_fb_form/shared/group_debug_icon.js.map +0 -1
- package/dist/components/hazo_fb_form/shared/index.d.ts +0 -10
- package/dist/components/hazo_fb_form/shared/index.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/shared/index.js +0 -9
- package/dist/components/hazo_fb_form/shared/index.js.map +0 -1
- package/dist/components/hazo_fb_form/shared/pdf_side_panel.d.ts +0 -22
- package/dist/components/hazo_fb_form/shared/pdf_side_panel.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/shared/pdf_side_panel.js +0 -10
- package/dist/components/hazo_fb_form/shared/pdf_side_panel.js.map +0 -1
- package/dist/components/hazo_fb_form/shared/send_back_item_card.d.ts +0 -44
- package/dist/components/hazo_fb_form/shared/send_back_item_card.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/shared/send_back_item_card.js +0 -80
- package/dist/components/hazo_fb_form/shared/send_back_item_card.js.map +0 -1
- package/dist/components/hazo_fb_form/shared/use_pdf_viewer.d.ts +0 -28
- package/dist/components/hazo_fb_form/shared/use_pdf_viewer.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/shared/use_pdf_viewer.js +0 -46
- package/dist/components/hazo_fb_form/shared/use_pdf_viewer.js.map +0 -1
- package/dist/components/hazo_fb_form/types.d.ts +0 -372
- package/dist/components/hazo_fb_form/types.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/types.js +0 -5
- package/dist/components/hazo_fb_form/types.js.map +0 -1
- package/dist/components/hazo_fb_form/views/back_office_view.d.ts +0 -7
- package/dist/components/hazo_fb_form/views/back_office_view.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/views/back_office_view.js +0 -425
- package/dist/components/hazo_fb_form/views/back_office_view.js.map +0 -1
- package/dist/components/hazo_fb_form/views/clarifications_view.d.ts +0 -16
- package/dist/components/hazo_fb_form/views/clarifications_view.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/views/clarifications_view.js +0 -291
- package/dist/components/hazo_fb_form/views/clarifications_view.js.map +0 -1
- package/dist/components/hazo_fb_form/views/client_data_view.d.ts +0 -6
- package/dist/components/hazo_fb_form/views/client_data_view.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/views/client_data_view.js +0 -39
- package/dist/components/hazo_fb_form/views/client_data_view.js.map +0 -1
- package/dist/components/hazo_fb_form/views/front_office_view.d.ts +0 -6
- package/dist/components/hazo_fb_form/views/front_office_view.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/views/front_office_view.js +0 -1351
- package/dist/components/hazo_fb_form/views/front_office_view.js.map +0 -1
- package/dist/components/hazo_fb_form/views/interim_view.d.ts +0 -8
- package/dist/components/hazo_fb_form/views/interim_view.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/views/interim_view.js +0 -535
- package/dist/components/hazo_fb_form/views/interim_view.js.map +0 -1
- package/dist/components/hazo_fb_form/views/review_queue_view.d.ts +0 -14
- package/dist/components/hazo_fb_form/views/review_queue_view.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/views/review_queue_view.js +0 -230
- package/dist/components/hazo_fb_form/views/review_queue_view.js.map +0 -1
- package/dist/components/hazo_fb_form/views/send_back_view.d.ts +0 -13
- package/dist/components/hazo_fb_form/views/send_back_view.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/views/send_back_view.js +0 -258
- package/dist/components/hazo_fb_form/views/send_back_view.js.map +0 -1
- package/dist/components/hazo_fb_form/views/summary_review_view.d.ts +0 -17
- package/dist/components/hazo_fb_form/views/summary_review_view.d.ts.map +0 -1
- package/dist/components/hazo_fb_form/views/summary_review_view.js +0 -258
- package/dist/components/hazo_fb_form/views/summary_review_view.js.map +0 -1
- package/dist/components/shared/json_data_panel/index.d.ts +0 -3
- package/dist/components/shared/json_data_panel/index.d.ts.map +0 -1
- package/dist/components/shared/json_data_panel/index.js +0 -2
- package/dist/components/shared/json_data_panel/index.js.map +0 -1
- package/dist/components/shared/json_data_panel/json_data_panel.d.ts +0 -28
- package/dist/components/shared/json_data_panel/json_data_panel.d.ts.map +0 -1
- package/dist/components/shared/json_data_panel/json_data_panel.js +0 -156
- package/dist/components/shared/json_data_panel/json_data_panel.js.map +0 -1
- package/dist/lib/fb_form_handler.d.ts +0 -63
- package/dist/lib/fb_form_handler.d.ts.map +0 -1
- package/dist/lib/fb_form_handler.js +0 -425
- package/dist/lib/fb_form_handler.js.map +0 -1
- package/dist/types/fb_form_data.d.ts.map +0 -1
- package/dist/types/fb_form_data.js.map +0 -1
- package/dist/types/fb_form_data_v1.d.ts +0 -250
- package/dist/types/fb_form_data_v1.d.ts.map +0 -1
- package/dist/types/fb_form_data_v1.js +0 -117
- package/dist/types/fb_form_data_v1.js.map +0 -1
- package/dist/types/fb_form_instance.d.ts +0 -49
- package/dist/types/fb_form_instance.d.ts.map +0 -1
- package/dist/types/fb_form_instance.js +0 -10
- package/dist/types/fb_form_instance.js.map +0 -1
- package/dist/utils/fb_data_adapter.d.ts +0 -33
- package/dist/utils/fb_data_adapter.d.ts.map +0 -1
- package/dist/utils/fb_data_adapter.js +0 -436
- package/dist/utils/fb_data_adapter.js.map +0 -1
- package/dist/utils/fb_data_adapter_v2.d.ts +0 -17
- package/dist/utils/fb_data_adapter_v2.d.ts.map +0 -1
- package/dist/utils/fb_data_adapter_v2.js +0 -483
- package/dist/utils/fb_data_adapter_v2.js.map +0 -1
- package/dist/utils/fb_data_helpers.d.ts +0 -86
- package/dist/utils/fb_data_helpers.d.ts.map +0 -1
- package/dist/utils/fb_data_helpers.js +0 -269
- package/dist/utils/fb_data_helpers.js.map +0 -1
- package/dist/utils/fb_data_mutations.d.ts +0 -43
- package/dist/utils/fb_data_mutations.d.ts.map +0 -1
- package/dist/utils/fb_data_mutations.js +0 -379
- package/dist/utils/fb_data_mutations.js.map +0 -1
- package/dist/utils/fb_data_mutations_v2.d.ts +0 -46
- package/dist/utils/fb_data_mutations_v2.d.ts.map +0 -1
- package/dist/utils/fb_data_mutations_v2.js +0 -341
- package/dist/utils/fb_data_mutations_v2.js.map +0 -1
- package/dist/utils/fb_data_queries.d.ts +0 -81
- package/dist/utils/fb_data_queries.d.ts.map +0 -1
- package/dist/utils/fb_data_queries.js +0 -354
- 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
|