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