hazo_collab_forms 2.2.7 → 3.0.1
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 +53 -0
- package/dist/components/_internal_form_set.d.ts +17 -0
- package/dist/components/_internal_form_set.d.ts.map +1 -1
- package/dist/components/_internal_form_set.js +38 -9
- package/dist/components/_internal_form_set.js.map +1 -1
- package/dist/components/clarification/clarification_card.d.ts +36 -13
- package/dist/components/clarification/clarification_card.d.ts.map +1 -1
- package/dist/components/clarification/clarification_card.js +12 -32
- package/dist/components/clarification/clarification_card.js.map +1 -1
- package/dist/components/clarification/clarification_dialog.d.ts.map +1 -1
- package/dist/components/clarification/clarification_dialog.js +11 -5
- package/dist/components/clarification/clarification_dialog.js.map +1 -1
- package/dist/components/clarification/clarification_doc_reference.d.ts +6 -0
- package/dist/components/clarification/clarification_doc_reference.d.ts.map +1 -1
- package/dist/components/clarification/clarification_doc_reference.js +20 -11
- package/dist/components/clarification/clarification_doc_reference.js.map +1 -1
- package/dist/components/clarification/clarification_group_card.d.ts +65 -0
- package/dist/components/clarification/clarification_group_card.d.ts.map +1 -0
- package/dist/components/clarification/clarification_group_card.js +84 -0
- package/dist/components/clarification/clarification_group_card.js.map +1 -0
- package/dist/components/clarification/clarification_item_body.d.ts +80 -0
- package/dist/components/clarification/clarification_item_body.d.ts.map +1 -0
- package/dist/components/clarification/clarification_item_body.js +55 -0
- package/dist/components/clarification/clarification_item_body.js.map +1 -0
- package/dist/components/clarification/clarification_response_form.d.ts +25 -1
- package/dist/components/clarification/clarification_response_form.d.ts.map +1 -1
- package/dist/components/clarification/clarification_response_form.js +103 -9
- package/dist/components/clarification/clarification_response_form.js.map +1 -1
- package/dist/components/clarification/clarification_section.d.ts +36 -2
- package/dist/components/clarification/clarification_section.d.ts.map +1 -1
- package/dist/components/clarification/clarification_section.js +55 -19
- package/dist/components/clarification/clarification_section.js.map +1 -1
- package/dist/components/clarification/clarification_status_config.d.ts +57 -0
- package/dist/components/clarification/clarification_status_config.d.ts.map +1 -0
- package/dist/components/clarification/clarification_status_config.js +131 -0
- package/dist/components/clarification/clarification_status_config.js.map +1 -0
- package/dist/components/clarification/index.d.ts +6 -0
- package/dist/components/clarification/index.d.ts.map +1 -1
- package/dist/components/clarification/index.js +3 -0
- package/dist/components/clarification/index.js.map +1 -1
- package/dist/components/collab_form_file_upload.d.ts +3 -1
- package/dist/components/collab_form_file_upload.d.ts.map +1 -1
- package/dist/components/collab_form_file_upload.js +6 -6
- package/dist/components/collab_form_file_upload.js.map +1 -1
- package/dist/components/hazo_add_field_dialog/components/pending_field_item.d.ts +14 -2
- package/dist/components/hazo_add_field_dialog/components/pending_field_item.d.ts.map +1 -1
- package/dist/components/hazo_add_field_dialog/components/pending_field_item.js +11 -9
- package/dist/components/hazo_add_field_dialog/components/pending_field_item.js.map +1 -1
- package/dist/components/hazo_add_field_dialog/components/pending_field_list.d.ts +14 -2
- package/dist/components/hazo_add_field_dialog/components/pending_field_list.d.ts.map +1 -1
- package/dist/components/hazo_add_field_dialog/components/pending_field_list.js +2 -2
- package/dist/components/hazo_add_field_dialog/components/pending_field_list.js.map +1 -1
- package/dist/components/hazo_add_field_dialog/components/question_input.d.ts +6 -1
- package/dist/components/hazo_add_field_dialog/components/question_input.d.ts.map +1 -1
- package/dist/components/hazo_add_field_dialog/components/question_input.js +2 -2
- package/dist/components/hazo_add_field_dialog/components/question_input.js.map +1 -1
- package/dist/components/hazo_add_field_dialog/hazo_add_field_dialog.d.ts +6 -1
- package/dist/components/hazo_add_field_dialog/hazo_add_field_dialog.d.ts.map +1 -1
- package/dist/components/hazo_add_field_dialog/hazo_add_field_dialog.js +67 -17
- package/dist/components/hazo_add_field_dialog/hazo_add_field_dialog.js.map +1 -1
- package/dist/components/hazo_add_field_dialog/index.d.ts +3 -1
- package/dist/components/hazo_add_field_dialog/index.d.ts.map +1 -1
- package/dist/components/hazo_add_field_dialog/index.js +2 -0
- package/dist/components/hazo_add_field_dialog/index.js.map +1 -1
- package/dist/components/hazo_add_field_dialog/options/file_textbox_options.d.ts +31 -0
- package/dist/components/hazo_add_field_dialog/options/file_textbox_options.d.ts.map +1 -0
- package/dist/components/hazo_add_field_dialog/options/file_textbox_options.js +54 -0
- package/dist/components/hazo_add_field_dialog/options/file_textbox_options.js.map +1 -0
- package/dist/components/hazo_add_field_dialog/options/index.d.ts +1 -0
- package/dist/components/hazo_add_field_dialog/options/index.d.ts.map +1 -1
- package/dist/components/hazo_add_field_dialog/options/index.js +1 -0
- package/dist/components/hazo_add_field_dialog/options/index.js.map +1 -1
- package/dist/components/hazo_add_field_dialog/types.d.ts +51 -2
- package/dist/components/hazo_add_field_dialog/types.d.ts.map +1 -1
- package/dist/components/hazo_add_field_dialog/types.js +7 -0
- package/dist/components/hazo_add_field_dialog/types.js.map +1 -1
- package/dist/components/hazo_collab_form_base.d.ts +6 -1
- package/dist/components/hazo_collab_form_base.d.ts.map +1 -1
- package/dist/components/hazo_collab_form_base.js +7 -15
- package/dist/components/hazo_collab_form_base.js.map +1 -1
- package/dist/components/hazo_collab_form_data_table.d.ts.map +1 -1
- package/dist/components/hazo_collab_form_data_table.js +4 -2
- package/dist/components/hazo_collab_form_data_table.js.map +1 -1
- package/dist/components/hazo_collab_form_file_textbox/file_chip.d.ts +37 -0
- package/dist/components/hazo_collab_form_file_textbox/file_chip.d.ts.map +1 -0
- package/dist/components/hazo_collab_form_file_textbox/file_chip.js +45 -0
- package/dist/components/hazo_collab_form_file_textbox/file_chip.js.map +1 -0
- package/dist/components/hazo_collab_form_file_textbox/file_group.d.ts +35 -0
- package/dist/components/hazo_collab_form_file_textbox/file_group.d.ts.map +1 -0
- package/dist/components/hazo_collab_form_file_textbox/file_group.js +54 -0
- package/dist/components/hazo_collab_form_file_textbox/file_group.js.map +1 -0
- package/dist/components/hazo_collab_form_file_textbox/file_textbox_panel.d.ts +46 -0
- package/dist/components/hazo_collab_form_file_textbox/file_textbox_panel.d.ts.map +1 -0
- package/dist/components/hazo_collab_form_file_textbox/file_textbox_panel.js +104 -0
- package/dist/components/hazo_collab_form_file_textbox/file_textbox_panel.js.map +1 -0
- package/dist/components/hazo_collab_form_file_textbox/file_upload_zone.d.ts +20 -0
- package/dist/components/hazo_collab_form_file_textbox/file_upload_zone.d.ts.map +1 -0
- package/dist/components/hazo_collab_form_file_textbox/file_upload_zone.js +14 -0
- package/dist/components/hazo_collab_form_file_textbox/file_upload_zone.js.map +1 -0
- package/dist/components/hazo_collab_form_file_textbox/hazo_collab_form_file_textbox.d.ts +63 -0
- package/dist/components/hazo_collab_form_file_textbox/hazo_collab_form_file_textbox.d.ts.map +1 -0
- package/dist/components/hazo_collab_form_file_textbox/hazo_collab_form_file_textbox.js +226 -0
- package/dist/components/hazo_collab_form_file_textbox/hazo_collab_form_file_textbox.js.map +1 -0
- package/dist/components/hazo_collab_form_file_textbox/index.d.ts +24 -0
- package/dist/components/hazo_collab_form_file_textbox/index.d.ts.map +1 -0
- package/dist/components/hazo_collab_form_file_textbox/index.js +16 -0
- package/dist/components/hazo_collab_form_file_textbox/index.js.map +1 -0
- package/dist/components/hazo_collab_form_file_textbox/tag_pills.d.ts +11 -0
- package/dist/components/hazo_collab_form_file_textbox/tag_pills.d.ts.map +1 -0
- package/dist/components/hazo_collab_form_file_textbox/tag_pills.js +17 -0
- package/dist/components/hazo_collab_form_file_textbox/tag_pills.js.map +1 -0
- package/dist/components/hazo_collab_form_file_textbox/utils.d.ts +14 -0
- package/dist/components/hazo_collab_form_file_textbox/utils.d.ts.map +1 -0
- package/dist/components/hazo_collab_form_file_textbox/utils.js +58 -0
- package/dist/components/hazo_collab_form_file_textbox/utils.js.map +1 -0
- package/dist/components/hazo_collab_form_file_textbox/validation_dialog.d.ts +29 -0
- package/dist/components/hazo_collab_form_file_textbox/validation_dialog.d.ts.map +1 -0
- package/dist/components/hazo_collab_form_file_textbox/validation_dialog.js +182 -0
- package/dist/components/hazo_collab_form_file_textbox/validation_dialog.js.map +1 -0
- package/dist/components/hazo_collab_form_group.d.ts.map +1 -1
- package/dist/components/hazo_collab_form_group.js +2 -2
- package/dist/components/hazo_collab_form_group.js.map +1 -1
- package/dist/components/hazo_collab_form_view/context.d.ts.map +1 -1
- package/dist/components/hazo_collab_form_view/context.js +10 -3
- package/dist/components/hazo_collab_form_view/context.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.map +1 -1
- package/dist/components/hazo_collab_form_view/types.d.ts +32 -4
- package/dist/components/hazo_collab_form_view/types.d.ts.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 +4 -2
- package/dist/components/hazo_collab_form_view/views/edit_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 +2 -2
- package/dist/components/hazo_collab_form_view/views/summary_view.js.map +1 -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 +4 -2
- package/dist/components/hazo_data_form/hazo_data_form.js.map +1 -1
- package/dist/components/hazo_data_form/pdf_panel.d.ts.map +1 -1
- package/dist/components/hazo_data_form/pdf_panel.js +7 -2
- package/dist/components/hazo_data_form/pdf_panel.js.map +1 -1
- package/dist/components/hazo_fb_form/components/backoffice_run_button.d.ts +16 -0
- package/dist/components/hazo_fb_form/components/backoffice_run_button.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/components/backoffice_run_button.js +23 -0
- package/dist/components/hazo_fb_form/components/backoffice_run_button.js.map +1 -0
- package/dist/components/hazo_fb_form/components/draft_clarification_card.d.ts +25 -0
- package/dist/components/hazo_fb_form/components/draft_clarification_card.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/components/draft_clarification_card.js +71 -0
- package/dist/components/hazo_fb_form/components/draft_clarification_card.js.map +1 -0
- package/dist/components/hazo_fb_form/components/fb_document_type_editor.d.ts +11 -0
- package/dist/components/hazo_fb_form/components/fb_document_type_editor.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/components/fb_document_type_editor.js +82 -0
- package/dist/components/hazo_fb_form/components/fb_document_type_editor.js.map +1 -0
- package/dist/components/hazo_fb_form/components/fb_tag_editor.d.ts +11 -0
- package/dist/components/hazo_fb_form/components/fb_tag_editor.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/components/fb_tag_editor.js +107 -0
- package/dist/components/hazo_fb_form/components/fb_tag_editor.js.map +1 -0
- package/dist/components/hazo_fb_form/components/front_office_stepper.d.ts +15 -0
- package/dist/components/hazo_fb_form/components/front_office_stepper.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/components/front_office_stepper.js +21 -0
- package/dist/components/hazo_fb_form/components/front_office_stepper.js.map +1 -0
- package/dist/components/hazo_fb_form/components/instance_sidebar.d.ts +21 -0
- package/dist/components/hazo_fb_form/components/instance_sidebar.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/components/instance_sidebar.js +58 -0
- package/dist/components/hazo_fb_form/components/instance_sidebar.js.map +1 -0
- package/dist/components/hazo_fb_form/components/run_button.d.ts +19 -0
- package/dist/components/hazo_fb_form/components/run_button.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/components/run_button.js +38 -0
- package/dist/components/hazo_fb_form/components/run_button.js.map +1 -0
- package/dist/components/hazo_fb_form/components/run_details_dialog.d.ts +17 -0
- package/dist/components/hazo_fb_form/components/run_details_dialog.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/components/run_details_dialog.js +35 -0
- package/dist/components/hazo_fb_form/components/run_details_dialog.js.map +1 -0
- package/dist/components/hazo_fb_form/components/tag_pill.d.ts +15 -0
- package/dist/components/hazo_fb_form/components/tag_pill.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/components/tag_pill.js +15 -0
- package/dist/components/hazo_fb_form/components/tag_pill.js.map +1 -0
- package/dist/components/hazo_fb_form/context.d.ts +90 -0
- package/dist/components/hazo_fb_form/context.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/context.js +13 -0
- package/dist/components/hazo_fb_form/context.js.map +1 -0
- package/dist/components/hazo_fb_form/hazo_fb_form.d.ts +13 -0
- package/dist/components/hazo_fb_form/hazo_fb_form.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/hazo_fb_form.js +401 -0
- package/dist/components/hazo_fb_form/hazo_fb_form.js.map +1 -0
- package/dist/components/hazo_fb_form/hooks/use_fb_form_state.d.ts +45 -0
- package/dist/components/hazo_fb_form/hooks/use_fb_form_state.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/hooks/use_fb_form_state.js +223 -0
- package/dist/components/hazo_fb_form/hooks/use_fb_form_state.js.map +1 -0
- package/dist/components/hazo_fb_form/hooks/use_llm_run.d.ts +46 -0
- package/dist/components/hazo_fb_form/hooks/use_llm_run.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/hooks/use_llm_run.js +1435 -0
- package/dist/components/hazo_fb_form/hooks/use_llm_run.js.map +1 -0
- package/dist/components/hazo_fb_form/index.d.ts +24 -0
- package/dist/components/hazo_fb_form/index.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/index.js +16 -0
- package/dist/components/hazo_fb_form/index.js.map +1 -0
- package/dist/components/hazo_fb_form/shared/clarification_helpers.d.ts +15 -0
- package/dist/components/hazo_fb_form/shared/clarification_helpers.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/shared/clarification_helpers.js +23 -0
- package/dist/components/hazo_fb_form/shared/clarification_helpers.js.map +1 -0
- package/dist/components/hazo_fb_form/shared/file_utils.d.ts +9 -0
- package/dist/components/hazo_fb_form/shared/file_utils.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/shared/file_utils.js +31 -0
- package/dist/components/hazo_fb_form/shared/file_utils.js.map +1 -0
- package/dist/components/hazo_fb_form/shared/format.d.ts +12 -0
- package/dist/components/hazo_fb_form/shared/format.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/shared/format.js +45 -0
- package/dist/components/hazo_fb_form/shared/format.js.map +1 -0
- package/dist/components/hazo_fb_form/shared/index.d.ts +10 -0
- package/dist/components/hazo_fb_form/shared/index.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/shared/index.js +9 -0
- package/dist/components/hazo_fb_form/shared/index.js.map +1 -0
- package/dist/components/hazo_fb_form/shared/pdf_side_panel.d.ts +22 -0
- package/dist/components/hazo_fb_form/shared/pdf_side_panel.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/shared/pdf_side_panel.js +10 -0
- package/dist/components/hazo_fb_form/shared/pdf_side_panel.js.map +1 -0
- package/dist/components/hazo_fb_form/shared/use_pdf_viewer.d.ts +26 -0
- package/dist/components/hazo_fb_form/shared/use_pdf_viewer.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/shared/use_pdf_viewer.js +41 -0
- package/dist/components/hazo_fb_form/shared/use_pdf_viewer.js.map +1 -0
- package/dist/components/hazo_fb_form/types.d.ts +283 -0
- package/dist/components/hazo_fb_form/types.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/types.js +5 -0
- package/dist/components/hazo_fb_form/types.js.map +1 -0
- package/dist/components/hazo_fb_form/views/back_office_view.d.ts +7 -0
- package/dist/components/hazo_fb_form/views/back_office_view.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/views/back_office_view.js +327 -0
- package/dist/components/hazo_fb_form/views/back_office_view.js.map +1 -0
- package/dist/components/hazo_fb_form/views/clarifications_view.d.ts +16 -0
- package/dist/components/hazo_fb_form/views/clarifications_view.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/views/clarifications_view.js +262 -0
- package/dist/components/hazo_fb_form/views/clarifications_view.js.map +1 -0
- package/dist/components/hazo_fb_form/views/front_office_view.d.ts +6 -0
- package/dist/components/hazo_fb_form/views/front_office_view.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/views/front_office_view.js +1097 -0
- package/dist/components/hazo_fb_form/views/front_office_view.js.map +1 -0
- package/dist/components/hazo_fb_form/views/interim_view.d.ts +8 -0
- package/dist/components/hazo_fb_form/views/interim_view.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/views/interim_view.js +416 -0
- package/dist/components/hazo_fb_form/views/interim_view.js.map +1 -0
- package/dist/components/hazo_fb_form/views/summary_review_view.d.ts +15 -0
- package/dist/components/hazo_fb_form/views/summary_review_view.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/views/summary_review_view.js +173 -0
- package/dist/components/hazo_fb_form/views/summary_review_view.js.map +1 -0
- package/dist/components/hazo_field_library/components/template_loader_dialog.d.ts.map +1 -1
- package/dist/components/hazo_field_library/components/template_loader_dialog.js +7 -7
- package/dist/components/hazo_field_library/components/template_loader_dialog.js.map +1 -1
- package/dist/components/hazo_field_library/hooks/use_elements.d.ts.map +1 -1
- package/dist/components/hazo_field_library/hooks/use_elements.js +0 -4
- package/dist/components/hazo_field_library/hooks/use_elements.js.map +1 -1
- package/dist/components/hazo_validation_rule_editor/components/clarification_settings.d.ts.map +1 -1
- package/dist/components/hazo_validation_rule_editor/components/clarification_settings.js +29 -2
- package/dist/components/hazo_validation_rule_editor/components/clarification_settings.js.map +1 -1
- package/dist/components/hazo_validation_rule_editor/components/rule_editor.d.ts.map +1 -1
- package/dist/components/hazo_validation_rule_editor/components/rule_editor.js +4 -4
- package/dist/components/hazo_validation_rule_editor/components/rule_editor.js.map +1 -1
- package/dist/components/hazo_validation_rule_editor/context.d.ts +3 -2
- package/dist/components/hazo_validation_rule_editor/context.d.ts.map +1 -1
- package/dist/components/hazo_validation_rule_editor/context.js +5 -1
- package/dist/components/hazo_validation_rule_editor/context.js.map +1 -1
- package/dist/components/hazo_validation_rule_editor/hooks/use_file_validation.d.ts.map +1 -1
- package/dist/components/hazo_validation_rule_editor/hooks/use_file_validation.js +23 -7
- package/dist/components/hazo_validation_rule_editor/hooks/use_file_validation.js.map +1 -1
- package/dist/components/hazo_validation_rule_editor/index.d.ts +1 -1
- package/dist/components/hazo_validation_rule_editor/index.d.ts.map +1 -1
- package/dist/components/hazo_validation_rule_editor/index.js.map +1 -1
- package/dist/components/hazo_validation_rule_editor/types.d.ts +10 -0
- package/dist/components/hazo_validation_rule_editor/types.d.ts.map +1 -1
- package/dist/components/hazo_validation_rule_editor/validation_rule_editor.d.ts +1 -1
- package/dist/components/hazo_validation_rule_editor/validation_rule_editor.d.ts.map +1 -1
- package/dist/components/hazo_validation_rule_editor/validation_rule_editor.js +2 -2
- package/dist/components/hazo_validation_rule_editor/validation_rule_editor.js.map +1 -1
- package/dist/components/index.d.ts +10 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +5 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/shared/editor_theme/components.d.ts.map +1 -1
- package/dist/components/shared/editor_theme/components.js +35 -3
- package/dist/components/shared/editor_theme/components.js.map +1 -1
- package/dist/components/shared/file_bar/file_bar.d.ts +51 -0
- package/dist/components/shared/file_bar/file_bar.d.ts.map +1 -0
- package/dist/components/shared/file_bar/file_bar.js +91 -0
- package/dist/components/shared/file_bar/file_bar.js.map +1 -0
- package/dist/components/shared/file_bar/file_bar_tag.d.ts +10 -0
- package/dist/components/shared/file_bar/file_bar_tag.d.ts.map +1 -0
- package/dist/components/shared/file_bar/file_bar_tag.js +15 -0
- package/dist/components/shared/file_bar/file_bar_tag.js.map +1 -0
- package/dist/components/shared/file_bar/file_bar_types.d.ts +29 -0
- package/dist/components/shared/file_bar/file_bar_types.d.ts.map +1 -0
- package/dist/components/shared/file_bar/file_bar_types.js +5 -0
- package/dist/components/shared/file_bar/file_bar_types.js.map +1 -0
- package/dist/components/shared/file_bar/file_bar_validation.d.ts +11 -0
- package/dist/components/shared/file_bar/file_bar_validation.d.ts.map +1 -0
- package/dist/components/shared/file_bar/file_bar_validation.js +21 -0
- package/dist/components/shared/file_bar/file_bar_validation.js.map +1 -0
- package/dist/components/shared/file_bar/file_bar_validation_dialog.d.ts +13 -0
- package/dist/components/shared/file_bar/file_bar_validation_dialog.d.ts.map +1 -0
- package/dist/components/shared/file_bar/file_bar_validation_dialog.js +74 -0
- package/dist/components/shared/file_bar/file_bar_validation_dialog.js.map +1 -0
- package/dist/components/shared/file_bar/file_validation_issues_dialog.d.ts +24 -0
- package/dist/components/shared/file_bar/file_validation_issues_dialog.d.ts.map +1 -0
- package/dist/components/shared/file_bar/file_validation_issues_dialog.js +22 -0
- package/dist/components/shared/file_bar/file_validation_issues_dialog.js.map +1 -0
- package/dist/components/shared/file_bar/index.d.ts +13 -0
- package/dist/components/shared/file_bar/index.d.ts.map +1 -0
- package/dist/components/shared/file_bar/index.js +8 -0
- package/dist/components/shared/file_bar/index.js.map +1 -0
- package/dist/components/shared/strip_base_props.d.ts.map +1 -1
- package/dist/components/shared/strip_base_props.js +2 -0
- package/dist/components/shared/strip_base_props.js.map +1 -1
- package/dist/components/shared/summary_files/summary_files.d.ts +2 -2
- package/dist/components/shared/summary_files/summary_files.d.ts.map +1 -1
- package/dist/components/shared/summary_files/summary_files.js +31 -72
- package/dist/components/shared/summary_files/summary_files.js.map +1 -1
- package/dist/components/shared/use_base_form_field.d.ts +1 -0
- package/dist/components/shared/use_base_form_field.d.ts.map +1 -1
- package/dist/components/shared/use_base_form_field.js +2 -1
- package/dist/components/shared/use_base_form_field.js.map +1 -1
- package/dist/config/clarification_templates.d.ts.map +1 -1
- package/dist/config/clarification_templates.js +15 -5
- package/dist/config/clarification_templates.js.map +1 -1
- package/dist/lib/autofill_handler.d.ts +42 -23
- package/dist/lib/autofill_handler.d.ts.map +1 -1
- package/dist/lib/autofill_handler.js +74 -197
- package/dist/lib/autofill_handler.js.map +1 -1
- package/dist/lib/fb_form_handler.d.ts +63 -0
- package/dist/lib/fb_form_handler.d.ts.map +1 -0
- package/dist/lib/fb_form_handler.js +232 -0
- package/dist/lib/fb_form_handler.js.map +1 -0
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +1 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/validation_handler.d.ts.map +1 -1
- package/dist/lib/validation_handler.js +153 -0
- package/dist/lib/validation_handler.js.map +1 -1
- package/dist/types/clarification.d.ts +14 -1
- package/dist/types/clarification.d.ts.map +1 -1
- package/dist/types/fb_form_instance.d.ts +44 -0
- package/dist/types/fb_form_instance.d.ts.map +1 -0
- package/dist/types/fb_form_instance.js +10 -0
- package/dist/types/fb_form_instance.js.map +1 -0
- package/dist/types/file_manager.d.ts +2 -0
- package/dist/types/file_manager.d.ts.map +1 -1
- package/dist/types/file_textbox.d.ts +36 -0
- package/dist/types/file_textbox.d.ts.map +1 -0
- package/dist/types/file_textbox.js +59 -0
- package/dist/types/file_textbox.js.map +1 -0
- package/dist/types/index.d.ts +6 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/validation.d.ts +34 -2
- package/dist/types/validation.d.ts.map +1 -1
- package/dist/utils/dev_file_manager.d.ts +12 -0
- package/dist/utils/dev_file_manager.d.ts.map +1 -0
- package/dist/utils/dev_file_manager.js +54 -0
- package/dist/utils/dev_file_manager.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +8 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/rule_to_execution.d.ts +7 -0
- package/dist/utils/rule_to_execution.d.ts.map +1 -0
- package/dist/utils/rule_to_execution.js +18 -0
- package/dist/utils/rule_to_execution.js.map +1 -0
- package/dist/utils/section_helpers.d.ts +20 -0
- package/dist/utils/section_helpers.d.ts.map +1 -0
- package/dist/utils/section_helpers.js +37 -0
- package/dist/utils/section_helpers.js.map +1 -0
- package/package.json +53 -23
|
@@ -0,0 +1,1097 @@
|
|
|
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 { SummaryReviewView } from './summary_review_view.js';
|
|
15
|
+
import { HazoUiDialog } from 'hazo_ui';
|
|
16
|
+
import { HiCheckCircle, HiExclamationCircle, HiChevronRight } from 'react-icons/hi';
|
|
17
|
+
import { rule_to_fb_execution } from '../../../utils/rule_to_execution.js';
|
|
18
|
+
import { FrontOfficeStepper } from '../components/front_office_stepper.js';
|
|
19
|
+
import { is_fb_terminal_status } from '../../../types/fb_form_instance.js';
|
|
20
|
+
// Lazy-load HazoAddFieldDialog to avoid pulling dnd-kit into the initial chunk
|
|
21
|
+
const LazyAddFieldDialog = React.lazy(() => import('../../hazo_add_field_dialog/hazo_add_field_dialog.js').then(mod => ({ default: mod.HazoAddFieldDialog })));
|
|
22
|
+
const LazyAddGroupDialog = React.lazy(() => import('../../hazo_add_group_dialog/hazo_add_group_dialog.js').then(mod => ({ default: mod.HazoAddGroupDialog })));
|
|
23
|
+
export function FrontOfficeView() {
|
|
24
|
+
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, } = useFbFormContext();
|
|
25
|
+
const { front_sections, front_form_data, on_front_change, field_textbox_configs, file_manager } = props;
|
|
26
|
+
const { on_add_front_field, on_add_front_group } = props;
|
|
27
|
+
const show_tags = props.show_classification_tags !== false; // default true
|
|
28
|
+
const can_add_fields = !!on_add_front_field;
|
|
29
|
+
const can_add_groups = !!on_add_front_group;
|
|
30
|
+
const [viewed_file, set_viewed_file] = useState(null);
|
|
31
|
+
const [PdfViewerComponent, set_pdf_viewer] = useState(null);
|
|
32
|
+
// Add field dialog state
|
|
33
|
+
const [add_field_open, set_add_field_open] = useState(false);
|
|
34
|
+
const [add_field_after_id, set_add_field_after_id] = useState('');
|
|
35
|
+
// Add group dialog state
|
|
36
|
+
const [add_group_open, set_add_group_open] = useState(false);
|
|
37
|
+
const [add_group_after_id, set_add_group_after_id] = useState('');
|
|
38
|
+
// Validation detail dialog state
|
|
39
|
+
const [validation_detail_file_id, set_validation_detail_file_id] = useState(null);
|
|
40
|
+
// Stepper state
|
|
41
|
+
const use_stepper = props.front_office_stepper !== false;
|
|
42
|
+
const is_past_instance = active_instance ? is_fb_terminal_status(active_instance.status) : false;
|
|
43
|
+
const [active_front_step, set_active_front_step] = useState(0);
|
|
44
|
+
const [completed_steps, set_completed_steps] = useState(new Set());
|
|
45
|
+
const [front_submitted, set_front_submitted] = useState(false);
|
|
46
|
+
const is_front_read_only = front_submitted || is_past_instance;
|
|
47
|
+
// Response validation state keyed by clarification_id
|
|
48
|
+
const [response_validations, set_response_validations] = useState({});
|
|
49
|
+
// Pipeline step tracking per file_id (for 3-step response validation flow)
|
|
50
|
+
const [file_pipeline_steps, set_file_pipeline_steps] = useState({});
|
|
51
|
+
// Cached prompt template for response validation
|
|
52
|
+
const response_validation_prompt_ref = useRef(null);
|
|
53
|
+
const prompt_fetch_promise_ref = useRef(null);
|
|
54
|
+
// Guard: in-flight validation per clarification_id (prevents concurrent calls)
|
|
55
|
+
const validating_ids_ref = useRef(new Set());
|
|
56
|
+
// Ref to current front_form_data to avoid stale closures in async callbacks
|
|
57
|
+
const front_form_data_ref = useRef(front_form_data);
|
|
58
|
+
front_form_data_ref.current = front_form_data;
|
|
59
|
+
// Lazy-load PdfViewer from hazo_pdf
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
let cancelled = false;
|
|
62
|
+
import(/* webpackIgnore: true */ 'hazo_pdf')
|
|
63
|
+
.then((mod) => {
|
|
64
|
+
if (!cancelled && mod.PdfViewer)
|
|
65
|
+
set_pdf_viewer(() => mod.PdfViewer);
|
|
66
|
+
})
|
|
67
|
+
.catch(() => { });
|
|
68
|
+
return () => { cancelled = true; };
|
|
69
|
+
}, []);
|
|
70
|
+
// ── Immediate validation on file upload ──
|
|
71
|
+
const has_validation_pipeline = !!props.validation_api_endpoint && (props.validation_rules?.length ?? 0) > 0;
|
|
72
|
+
const has_classification_pipeline = !!props.llm_api_endpoint && !!field_textbox_configs;
|
|
73
|
+
// Trigger immediate validation for a single file
|
|
74
|
+
const trigger_validate_file = useCallback(async (field_id, attachment) => {
|
|
75
|
+
console.log('[trigger_validate_file] Called:', {
|
|
76
|
+
field_id,
|
|
77
|
+
file_name: attachment.file_name,
|
|
78
|
+
has_endpoint: !!props.validation_api_endpoint,
|
|
79
|
+
rules_count: props.validation_rules?.length ?? 0,
|
|
80
|
+
});
|
|
81
|
+
if (!props.validation_api_endpoint || !props.validation_rules?.length) {
|
|
82
|
+
console.log('[trigger_validate_file] Skipping — no endpoint or rules');
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// Check for unsupported file types (images, video, audio)
|
|
86
|
+
const mime = attachment.mime_type?.toLowerCase() ?? '';
|
|
87
|
+
if (mime.startsWith('image/') || mime.startsWith('video/') || mime.startsWith('audio/')) {
|
|
88
|
+
const type_label = mime.startsWith('image/') ? 'image' : mime.startsWith('video/') ? 'video' : 'audio';
|
|
89
|
+
set_file_validation_results(prev => ({
|
|
90
|
+
...prev,
|
|
91
|
+
[attachment.file_id]: {
|
|
92
|
+
file_id: attachment.file_id,
|
|
93
|
+
file_name: attachment.file_name,
|
|
94
|
+
status: 'failed',
|
|
95
|
+
errors: [],
|
|
96
|
+
document_types: [],
|
|
97
|
+
failure_reason: `This file type (${mime}) cannot be validated. Only PDF and text-based documents are supported for automated validation.`,
|
|
98
|
+
},
|
|
99
|
+
}));
|
|
100
|
+
// Update pipeline step if in a pipeline
|
|
101
|
+
set_file_pipeline_steps(prev => {
|
|
102
|
+
const steps = prev[attachment.file_id];
|
|
103
|
+
if (!steps)
|
|
104
|
+
return prev;
|
|
105
|
+
return {
|
|
106
|
+
...prev,
|
|
107
|
+
[attachment.file_id]: steps.map(s => s.id === 'doc_type_validation'
|
|
108
|
+
? { ...s, status: 'failed', error: `Unsupported ${type_label} file type for validation` }
|
|
109
|
+
: s),
|
|
110
|
+
};
|
|
111
|
+
});
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
// Only run rules with check_type 'immediate' or unset (backward compat)
|
|
115
|
+
const rules = props.validation_rules.filter(r => r.enabled && (!r.check_type || r.check_type === 'immediate'));
|
|
116
|
+
console.log('[trigger_validate_file] Filtered rules:', rules.length, 'from', props.validation_rules.length);
|
|
117
|
+
if (rules.length === 0) {
|
|
118
|
+
console.log('[trigger_validate_file] No matching rules, returning');
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
// Mark as validating
|
|
122
|
+
set_validating_file_ids(prev => { const s = new Set(prev); s.add(attachment.file_id); return s; });
|
|
123
|
+
try {
|
|
124
|
+
const download_url = file_manager?.callbacks?.get_download_url?.(attachment.file_id, 'public') ?? '';
|
|
125
|
+
const rules_to_execute = rules.map(rule_to_fb_execution);
|
|
126
|
+
console.log('[trigger_validate_file] POSTing to', props.validation_api_endpoint, 'with', rules_to_execute.length, 'rules');
|
|
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.has_issue) ? '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
|
|
293
|
+
const files = attachments.map(att => ({
|
|
294
|
+
file_id: att.file_id,
|
|
295
|
+
file_name: att.file_name,
|
|
296
|
+
mime_type: att.mime_type,
|
|
297
|
+
download_url: file_manager?.callbacks?.get_download_url?.(att.file_id, 'public') ?? '',
|
|
298
|
+
}));
|
|
299
|
+
const body = {
|
|
300
|
+
file_name: files[0].file_name,
|
|
301
|
+
mime_type: files[0].mime_type,
|
|
302
|
+
rules: [{
|
|
303
|
+
rule_id: 'response_validation',
|
|
304
|
+
name: 'Response Validation',
|
|
305
|
+
prompt,
|
|
306
|
+
target_field_id: item.target_field_id,
|
|
307
|
+
target_label: item.target_label,
|
|
308
|
+
clarification_type: 'invalid_document',
|
|
309
|
+
}],
|
|
310
|
+
variables,
|
|
311
|
+
};
|
|
312
|
+
if (is_batch) {
|
|
313
|
+
body.mode = 'batch';
|
|
314
|
+
body.files = files;
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
body.download_url = files[0].download_url;
|
|
318
|
+
}
|
|
319
|
+
const res = await fetch(validation_endpoint, {
|
|
320
|
+
method: 'POST',
|
|
321
|
+
headers: { 'Content-Type': 'application/json' },
|
|
322
|
+
body: JSON.stringify(body),
|
|
323
|
+
});
|
|
324
|
+
const result = await res.json();
|
|
325
|
+
const rule_result = result.rule_results?.[0];
|
|
326
|
+
const passed = !rule_result?.has_issue;
|
|
327
|
+
// Enrich clarification doc_references with actual file_ids
|
|
328
|
+
const clarifications = (result.clarifications ?? []).map((c) => ({
|
|
329
|
+
...c,
|
|
330
|
+
doc_references: c.doc_references.map((ref, idx) => ({
|
|
331
|
+
...ref,
|
|
332
|
+
file_id: ref.file_id || attachments[idx]?.file_id || attachments[0]?.file_id || '',
|
|
333
|
+
})),
|
|
334
|
+
}));
|
|
335
|
+
// Use issue_description when failed, or raw_response parsed validation_details when passed
|
|
336
|
+
let details = rule_result?.issue_description;
|
|
337
|
+
if (!details && rule_result?.raw_response) {
|
|
338
|
+
try {
|
|
339
|
+
const raw = typeof rule_result.raw_response === 'string' ? JSON.parse(rule_result.raw_response) : rule_result.raw_response;
|
|
340
|
+
details = raw?.validation_details;
|
|
341
|
+
}
|
|
342
|
+
catch { /* ignore parse errors */ }
|
|
343
|
+
}
|
|
344
|
+
return { passed, details, rule_result, clarifications };
|
|
345
|
+
}
|
|
346
|
+
catch (err) {
|
|
347
|
+
console.error('[FrontOfficeView] Response batch validation error:', err);
|
|
348
|
+
return { passed: false, details: err instanceof Error ? err.message : 'Validation request failed' };
|
|
349
|
+
}
|
|
350
|
+
}, [props.validation_api_endpoint, get_response_validation_prompt, file_manager]);
|
|
351
|
+
/** Helper to update a single pipeline step for a file */
|
|
352
|
+
const update_pipeline_step = useCallback((file_id, step_id, update) => {
|
|
353
|
+
set_file_pipeline_steps(prev => {
|
|
354
|
+
const steps = prev[file_id];
|
|
355
|
+
if (!steps)
|
|
356
|
+
return prev;
|
|
357
|
+
return {
|
|
358
|
+
...prev,
|
|
359
|
+
[file_id]: steps.map(s => s.id === step_id ? { ...s, ...update } : s),
|
|
360
|
+
};
|
|
361
|
+
});
|
|
362
|
+
}, []);
|
|
363
|
+
/** Validate all files in a clarification response.
|
|
364
|
+
* 3-step pipeline: Response Validation → Classification → Doc Type Validation.
|
|
365
|
+
* Stores per-file results in file_validation_results (for file bar badges + dialog). */
|
|
366
|
+
const validate_clarification_response = useCallback(async (clarification_id, item, response) => {
|
|
367
|
+
if (!response.response_files?.length)
|
|
368
|
+
return true;
|
|
369
|
+
// Guard against concurrent validation for the same clarification
|
|
370
|
+
if (validating_ids_ref.current.has(clarification_id))
|
|
371
|
+
return false;
|
|
372
|
+
validating_ids_ref.current.add(clarification_id);
|
|
373
|
+
set_response_validations(prev => ({
|
|
374
|
+
...prev,
|
|
375
|
+
[clarification_id]: { status: 'validating' },
|
|
376
|
+
}));
|
|
377
|
+
// Initialize 3-step pipeline for each response file
|
|
378
|
+
for (const att of response.response_files) {
|
|
379
|
+
set_validating_file_ids(prev => { const s = new Set(prev); s.add(att.file_id); return s; });
|
|
380
|
+
set_file_validation_results(prev => ({
|
|
381
|
+
...prev,
|
|
382
|
+
[att.file_id]: {
|
|
383
|
+
file_id: att.file_id,
|
|
384
|
+
file_name: att.file_name,
|
|
385
|
+
status: 'validating',
|
|
386
|
+
errors: [],
|
|
387
|
+
document_types: [],
|
|
388
|
+
rule_results: [],
|
|
389
|
+
},
|
|
390
|
+
}));
|
|
391
|
+
set_file_pipeline_steps(prev => ({
|
|
392
|
+
...prev,
|
|
393
|
+
[att.file_id]: [
|
|
394
|
+
{ id: 'response_validation', label: 'Response Validation', status: 'running' },
|
|
395
|
+
{ id: 'classification', label: 'Document Classification', status: 'pending' },
|
|
396
|
+
{ id: 'doc_type_validation', label: 'Document Type Validation', status: 'pending' },
|
|
397
|
+
],
|
|
398
|
+
}));
|
|
399
|
+
}
|
|
400
|
+
try {
|
|
401
|
+
const all_details = [];
|
|
402
|
+
// ── Step 1: Response Validation (batch — all files evaluated collectively) ──
|
|
403
|
+
console.log('[ResponsePipeline] Step 1: Response Validation (batch) for', response.response_files.length, 'files');
|
|
404
|
+
const batch_result = await validate_response_batch(item, response);
|
|
405
|
+
console.log('[ResponsePipeline] Step 1 batch result:', { passed: batch_result.passed, details: batch_result.details?.substring(0, 100) });
|
|
406
|
+
// Apply batch result to ALL files uniformly
|
|
407
|
+
for (const att of response.response_files) {
|
|
408
|
+
update_pipeline_step(att.file_id, 'response_validation', {
|
|
409
|
+
status: batch_result.passed ? 'passed' : 'failed',
|
|
410
|
+
details: batch_result.details,
|
|
411
|
+
error: batch_result.passed ? undefined : batch_result.details,
|
|
412
|
+
});
|
|
413
|
+
// Store per-file result — clarifications only on the first file to avoid duplicates
|
|
414
|
+
const is_first = att.file_id === response.response_files[0].file_id;
|
|
415
|
+
set_file_validation_results(prev => ({
|
|
416
|
+
...prev,
|
|
417
|
+
[att.file_id]: {
|
|
418
|
+
file_id: att.file_id,
|
|
419
|
+
file_name: att.file_name,
|
|
420
|
+
status: batch_result.passed ? 'passed' : 'failed',
|
|
421
|
+
errors: is_first ? (batch_result.clarifications ?? []) : [],
|
|
422
|
+
document_types: [],
|
|
423
|
+
rule_results: batch_result.rule_result ? [{
|
|
424
|
+
...batch_result.rule_result,
|
|
425
|
+
rule_name: 'Response Validation',
|
|
426
|
+
}] : [],
|
|
427
|
+
},
|
|
428
|
+
}));
|
|
429
|
+
if (!batch_result.passed) {
|
|
430
|
+
update_pipeline_step(att.file_id, 'classification', { status: 'skipped' });
|
|
431
|
+
update_pipeline_step(att.file_id, 'doc_type_validation', { status: 'skipped' });
|
|
432
|
+
set_validating_file_ids(prev => { const s = new Set(prev); s.delete(att.file_id); return s; });
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
if (batch_result.details) {
|
|
436
|
+
all_details.push(batch_result.details);
|
|
437
|
+
}
|
|
438
|
+
if (!batch_result.passed) {
|
|
439
|
+
set_response_validations(prev => ({
|
|
440
|
+
...prev,
|
|
441
|
+
[clarification_id]: {
|
|
442
|
+
status: 'failed',
|
|
443
|
+
error: batch_result.details || 'Response validation failed',
|
|
444
|
+
},
|
|
445
|
+
}));
|
|
446
|
+
return false;
|
|
447
|
+
}
|
|
448
|
+
// ── Step 2: Classification → Step 3: Validation ──
|
|
449
|
+
// trigger_classify_file needs a field_textbox_config. Resolve the source field_id
|
|
450
|
+
// from the clarification's doc_references (the original field the file belongs to).
|
|
451
|
+
const source_field_id = item.doc_references?.[0]?.source_field_id;
|
|
452
|
+
const classify_field_id = source_field_id && field_textbox_configs?.[source_field_id]
|
|
453
|
+
? source_field_id
|
|
454
|
+
: Object.keys(field_textbox_configs ?? {})[0]; // fallback to first field
|
|
455
|
+
const can_classify = has_classification_pipeline && !!classify_field_id && !!field_textbox_configs?.[classify_field_id];
|
|
456
|
+
for (const att of response.response_files) {
|
|
457
|
+
if (can_classify) {
|
|
458
|
+
// Classification → validation (trigger_classify_file chains to validation internally)
|
|
459
|
+
update_pipeline_step(att.file_id, 'classification', { status: 'running' });
|
|
460
|
+
trigger_classify_file(classify_field_id, att.file_id, att.file_name);
|
|
461
|
+
}
|
|
462
|
+
else if (has_validation_pipeline) {
|
|
463
|
+
// No classification config — skip to validation
|
|
464
|
+
update_pipeline_step(att.file_id, 'classification', { status: 'skipped' });
|
|
465
|
+
update_pipeline_step(att.file_id, 'doc_type_validation', { status: 'running' });
|
|
466
|
+
trigger_validate_file(item.target_field_id, att);
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
update_pipeline_step(att.file_id, 'classification', { status: 'skipped' });
|
|
470
|
+
update_pipeline_step(att.file_id, 'doc_type_validation', { status: 'skipped' });
|
|
471
|
+
set_validating_file_ids(prev => { const s = new Set(prev); s.delete(att.file_id); return s; });
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
set_response_validations(prev => ({
|
|
475
|
+
...prev,
|
|
476
|
+
[clarification_id]: {
|
|
477
|
+
status: 'passed',
|
|
478
|
+
message: all_details.length > 0 ? all_details.join('; ') : 'Response files validated',
|
|
479
|
+
},
|
|
480
|
+
}));
|
|
481
|
+
return true;
|
|
482
|
+
}
|
|
483
|
+
catch (err) {
|
|
484
|
+
// Clear validating state for all files on error
|
|
485
|
+
for (const att of (response.response_files ?? [])) {
|
|
486
|
+
set_validating_file_ids(prev => { const s = new Set(prev); s.delete(att.file_id); return s; });
|
|
487
|
+
// Mark all pending steps as failed
|
|
488
|
+
update_pipeline_step(att.file_id, 'response_validation', { status: 'failed', error: 'Pipeline error' });
|
|
489
|
+
update_pipeline_step(att.file_id, 'classification', { status: 'failed', error: 'Pipeline error' });
|
|
490
|
+
update_pipeline_step(att.file_id, 'doc_type_validation', { status: 'failed', error: 'Pipeline error' });
|
|
491
|
+
}
|
|
492
|
+
set_response_validations(prev => ({
|
|
493
|
+
...prev,
|
|
494
|
+
[clarification_id]: {
|
|
495
|
+
status: 'failed',
|
|
496
|
+
error: err instanceof Error ? err.message : 'Validation failed',
|
|
497
|
+
},
|
|
498
|
+
}));
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
finally {
|
|
502
|
+
validating_ids_ref.current.delete(clarification_id);
|
|
503
|
+
}
|
|
504
|
+
}, [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]);
|
|
505
|
+
// ── Reactive pipeline step updates based on classifying/validating state changes ──
|
|
506
|
+
useEffect(() => {
|
|
507
|
+
set_file_pipeline_steps(prev => {
|
|
508
|
+
let changed = false;
|
|
509
|
+
const next = { ...prev };
|
|
510
|
+
for (const [file_id, steps] of Object.entries(next)) {
|
|
511
|
+
const cls_step = steps.find(s => s.id === 'classification');
|
|
512
|
+
const val_step = steps.find(s => s.id === 'doc_type_validation');
|
|
513
|
+
// Classification finished → mark passed, validation will start via trigger_classify_file's internal chaining
|
|
514
|
+
if (cls_step?.status === 'running' && !classifying_file_ids.has(file_id)) {
|
|
515
|
+
changed = true;
|
|
516
|
+
next[file_id] = steps.map(s => {
|
|
517
|
+
if (s.id === 'classification')
|
|
518
|
+
return { ...s, status: 'passed', details: 'Document classified' };
|
|
519
|
+
if (s.id === 'doc_type_validation' && s.status === 'pending')
|
|
520
|
+
return { ...s, status: 'running' };
|
|
521
|
+
return s;
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
if (val_step?.status === 'running' && !validating_file_ids.has(file_id)) {
|
|
525
|
+
// Validation finished — check result
|
|
526
|
+
const vr = file_validation_results[file_id];
|
|
527
|
+
changed = true;
|
|
528
|
+
next[file_id] = steps.map(s => {
|
|
529
|
+
if (s.id === 'doc_type_validation') {
|
|
530
|
+
const passed = vr?.status === 'passed' || vr?.status === 'skipped';
|
|
531
|
+
const checks_run = vr?.rule_results?.length ?? 0;
|
|
532
|
+
const checks_failed = vr?.rule_results?.filter(r => r.has_issue).length ?? 0;
|
|
533
|
+
return {
|
|
534
|
+
...s,
|
|
535
|
+
status: (passed ? 'passed' : 'failed'),
|
|
536
|
+
details: passed
|
|
537
|
+
? `${checks_run} check${checks_run !== 1 ? 's' : ''} passed`
|
|
538
|
+
: `${checks_failed} of ${checks_run} check${checks_run !== 1 ? 's' : ''} failed`,
|
|
539
|
+
error: !passed ? (vr?.failure_reason || undefined) : undefined,
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
return s;
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
return changed ? next : prev;
|
|
547
|
+
});
|
|
548
|
+
}, [classifying_file_ids, validating_file_ids, file_validation_results]);
|
|
549
|
+
const handle_open_add_field = useCallback((after_field_id) => {
|
|
550
|
+
set_add_field_after_id(after_field_id);
|
|
551
|
+
set_add_field_open(true);
|
|
552
|
+
}, []);
|
|
553
|
+
const handle_confirm_add_fields = useCallback((fields) => {
|
|
554
|
+
if (!on_add_front_field)
|
|
555
|
+
return;
|
|
556
|
+
for (const field of fields) {
|
|
557
|
+
on_add_front_field(add_field_after_id, field);
|
|
558
|
+
}
|
|
559
|
+
}, [on_add_front_field, add_field_after_id]);
|
|
560
|
+
const handle_open_add_group = useCallback((after_group_id) => {
|
|
561
|
+
set_add_group_after_id(after_group_id);
|
|
562
|
+
set_add_group_open(true);
|
|
563
|
+
}, []);
|
|
564
|
+
const handle_confirm_add_group = useCallback((group) => {
|
|
565
|
+
if (!on_add_front_group)
|
|
566
|
+
return;
|
|
567
|
+
on_add_front_group(add_group_after_id, group);
|
|
568
|
+
}, [on_add_front_group, add_group_after_id]);
|
|
569
|
+
// Build per-file tag maps from per-file classifications
|
|
570
|
+
const per_field_file_tags = useMemo(() => {
|
|
571
|
+
if (!show_tags)
|
|
572
|
+
return {};
|
|
573
|
+
const result = {};
|
|
574
|
+
for (const classification of classification_results) {
|
|
575
|
+
const config = field_textbox_configs?.[classification.field_id];
|
|
576
|
+
if (!classification.file_classifications)
|
|
577
|
+
continue;
|
|
578
|
+
const available_tags = config?.classification.available_tags ?? props.available_tags ?? [];
|
|
579
|
+
const file_map = {};
|
|
580
|
+
for (const fc of classification.file_classifications) {
|
|
581
|
+
const matched = available_tags
|
|
582
|
+
.filter((t) => fc.tags.includes(t.tag_id))
|
|
583
|
+
.map((t) => ({ id: t.tag_id, label: t.tag_label, color: t.color }));
|
|
584
|
+
if (matched.length > 0) {
|
|
585
|
+
file_map[fc.file_id] = matched;
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
// File was classified but matched no tags — show "Unknown" with reason tooltip
|
|
589
|
+
file_map[fc.file_id] = [{
|
|
590
|
+
id: '__unknown',
|
|
591
|
+
label: 'Unknown',
|
|
592
|
+
color: 'bg-gray-100 text-gray-600',
|
|
593
|
+
tooltip: fc.unclassified_reason || 'Could not match to any classification category',
|
|
594
|
+
}];
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
if (Object.keys(file_map).length > 0) {
|
|
598
|
+
result[classification.field_id] = file_map;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return result;
|
|
602
|
+
}, [classification_results, field_textbox_configs, show_tags]);
|
|
603
|
+
// Build per-file classification metadata (document_date, document_nature)
|
|
604
|
+
const per_field_file_metadata = useMemo(() => {
|
|
605
|
+
if (!show_tags)
|
|
606
|
+
return {};
|
|
607
|
+
const result = {};
|
|
608
|
+
for (const classification of classification_results) {
|
|
609
|
+
if (!classification.file_classifications)
|
|
610
|
+
continue;
|
|
611
|
+
const meta_map = {};
|
|
612
|
+
for (const fc of classification.file_classifications) {
|
|
613
|
+
if (fc.document_date || fc.document_nature || fc.document_type?.length) {
|
|
614
|
+
meta_map[fc.file_id] = {
|
|
615
|
+
document_date: fc.document_date,
|
|
616
|
+
document_nature: fc.document_nature,
|
|
617
|
+
document_type: fc.document_type,
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
if (Object.keys(meta_map).length > 0) {
|
|
622
|
+
result[classification.field_id] = meta_map;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
return result;
|
|
626
|
+
}, [classification_results, show_tags]);
|
|
627
|
+
// Build set of responded/resolved clarification IDs (from both pending + sent)
|
|
628
|
+
const responded_clarification_ids = useMemo(() => {
|
|
629
|
+
const ids = new Set();
|
|
630
|
+
// From pending responses (not yet flushed)
|
|
631
|
+
for (const [clar_id, response] of pending_clarification_responses) {
|
|
632
|
+
if (response.response_choice)
|
|
633
|
+
ids.add(clar_id);
|
|
634
|
+
}
|
|
635
|
+
// From sent/flushed clarifications
|
|
636
|
+
for (const item of sent_clarifications) {
|
|
637
|
+
if (item.status === 'responded' || item.status === 'resolved') {
|
|
638
|
+
ids.add(item.id);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
return ids;
|
|
642
|
+
}, [pending_clarification_responses, sent_clarifications]);
|
|
643
|
+
// Build per-file validation errors map from file_validation_results (all errors, including responded)
|
|
644
|
+
// — the chip needs all errors to show the response label; counting is handled separately
|
|
645
|
+
const per_file_validation_errors = useMemo(() => {
|
|
646
|
+
const result = {};
|
|
647
|
+
for (const [file_id, vr] of Object.entries(file_validation_results)) {
|
|
648
|
+
if (vr.errors && vr.errors.length > 0) {
|
|
649
|
+
result[file_id] = vr.errors;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
return result;
|
|
653
|
+
}, [file_validation_results]);
|
|
654
|
+
// Set of file IDs that passed validation
|
|
655
|
+
const validated_file_ids = useMemo(() => {
|
|
656
|
+
const ids = new Set();
|
|
657
|
+
for (const [file_id, vr] of Object.entries(file_validation_results)) {
|
|
658
|
+
if (vr.status === 'passed')
|
|
659
|
+
ids.add(file_id);
|
|
660
|
+
}
|
|
661
|
+
return ids;
|
|
662
|
+
}, [file_validation_results]);
|
|
663
|
+
// Compute file response labels — only set when ALL errors for a file are responded to.
|
|
664
|
+
// Used by file chips to show "N Resolved" (amber) vs "N issues" (red).
|
|
665
|
+
const external_file_response_labels = useMemo(() => {
|
|
666
|
+
// Get ALL errors per file (including responded ones) from file_validation_results
|
|
667
|
+
const all_errors_by_file = {};
|
|
668
|
+
for (const [file_id, vr] of Object.entries(file_validation_results)) {
|
|
669
|
+
if (vr.errors && vr.errors.length > 0) {
|
|
670
|
+
all_errors_by_file[file_id] = vr.errors;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
const labels = {};
|
|
674
|
+
for (const [file_id, errors] of Object.entries(all_errors_by_file)) {
|
|
675
|
+
// Check if ALL errors for this file have been responded to
|
|
676
|
+
const all_resolved = errors.every(e => responded_clarification_ids.has(e.id));
|
|
677
|
+
if (all_resolved) {
|
|
678
|
+
labels[file_id] = 'resolved';
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
return Object.keys(labels).length > 0 ? labels : undefined;
|
|
682
|
+
}, [file_validation_results, responded_clarification_ids]);
|
|
683
|
+
// Compute per-file pipeline status text from active running steps + classifying/validating state
|
|
684
|
+
const per_file_pipeline_status = useMemo(() => {
|
|
685
|
+
const result = {};
|
|
686
|
+
// Pipeline steps take priority
|
|
687
|
+
for (const [file_id, steps] of Object.entries(file_pipeline_steps)) {
|
|
688
|
+
const active = steps.find(s => s.status === 'running');
|
|
689
|
+
if (active) {
|
|
690
|
+
result[file_id] = `${active.label}...`;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
// For non-pipeline files, show status from validating/classifying state (validating takes priority)
|
|
694
|
+
for (const file_id of validating_file_ids) {
|
|
695
|
+
if (!result[file_id]) {
|
|
696
|
+
result[file_id] = 'Validating document...';
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
for (const file_id of classifying_file_ids) {
|
|
700
|
+
if (!result[file_id]) {
|
|
701
|
+
result[file_id] = 'Classifying document...';
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return result;
|
|
705
|
+
}, [file_pipeline_steps, classifying_file_ids, validating_file_ids]);
|
|
706
|
+
// Build set of backoffice-only rule IDs to exclude from front office display
|
|
707
|
+
const backoffice_rule_ids = useMemo(() => {
|
|
708
|
+
const ids = new Set();
|
|
709
|
+
for (const rule of props.validation_rules ?? []) {
|
|
710
|
+
if (rule.check_type === 'backoffice')
|
|
711
|
+
ids.add(rule.rule_id);
|
|
712
|
+
}
|
|
713
|
+
return ids;
|
|
714
|
+
}, [props.validation_rules]);
|
|
715
|
+
// Immediate validation clarifications enriched with any pending responses + sent responses
|
|
716
|
+
// so ClarificationSection cards reflect selections made in the inline file dialog (and vice versa)
|
|
717
|
+
// Excludes drafts from backoffice-only rules (those only appear in the Back Office tab)
|
|
718
|
+
const immediate_clarifications = useMemo(() => {
|
|
719
|
+
return draft_clarifications
|
|
720
|
+
.filter(item => !item.rule_id || !backoffice_rule_ids.has(item.rule_id))
|
|
721
|
+
.map(item => {
|
|
722
|
+
// Check pending responses first (not yet flushed)
|
|
723
|
+
const pending = pending_clarification_responses.get(item.id);
|
|
724
|
+
if (pending) {
|
|
725
|
+
return {
|
|
726
|
+
...item,
|
|
727
|
+
status: pending.response_choice ? 'responded' : item.status,
|
|
728
|
+
response_choice: pending.response_choice ?? item.response_choice,
|
|
729
|
+
user_comment: pending.user_comment ?? item.user_comment,
|
|
730
|
+
response_files: pending.response_files ?? item.response_files,
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
// Check sent/flushed clarifications for responses
|
|
734
|
+
const sent = sent_clarifications.find(c => c.id === item.id);
|
|
735
|
+
if (sent && (sent.status === 'responded' || sent.status === 'resolved')) {
|
|
736
|
+
return {
|
|
737
|
+
...item,
|
|
738
|
+
status: sent.status,
|
|
739
|
+
response_choice: sent.response_choice ?? item.response_choice,
|
|
740
|
+
user_comment: sent.user_comment ?? item.user_comment,
|
|
741
|
+
response_files: sent.response_files ?? item.response_files,
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
return item;
|
|
745
|
+
});
|
|
746
|
+
}, [draft_clarifications, pending_clarification_responses, sent_clarifications, backoffice_rule_ids]);
|
|
747
|
+
const immediate_clarification_counts = useMemo(() => {
|
|
748
|
+
const counts = { pending: 0, responded: 0, resolved: 0, dismissed: 0, total: 0 };
|
|
749
|
+
for (const item of immediate_clarifications) {
|
|
750
|
+
counts[item.status]++;
|
|
751
|
+
counts.total++;
|
|
752
|
+
}
|
|
753
|
+
return counts;
|
|
754
|
+
}, [immediate_clarifications]);
|
|
755
|
+
// Compute per-file validation details for built-in FileBar dialog
|
|
756
|
+
// Skip files where all checks failed and clarifications exist — those open ClarificationDialog via on_validation_click instead
|
|
757
|
+
const per_file_validation_details = useMemo(() => {
|
|
758
|
+
const result = {};
|
|
759
|
+
for (const [file_id, vr] of Object.entries(file_validation_results)) {
|
|
760
|
+
const rr = vr.rule_results ?? [];
|
|
761
|
+
const has_only_failures = rr.length > 0 && rr.every(r => r.has_issue);
|
|
762
|
+
if (has_only_failures) {
|
|
763
|
+
const has_clarifications = immediate_clarifications.some(item => item.doc_references?.some(r => r.file_id === file_id));
|
|
764
|
+
if (has_clarifications)
|
|
765
|
+
continue; // Skip — ClarificationDialog handles this
|
|
766
|
+
}
|
|
767
|
+
const steps = file_pipeline_steps[file_id];
|
|
768
|
+
result[file_id] = {
|
|
769
|
+
file_name: vr.file_name,
|
|
770
|
+
overall_status: vr.status === 'passed' || vr.status === 'skipped' ? 'passed'
|
|
771
|
+
: vr.status === 'validating' ? 'validating'
|
|
772
|
+
: 'failed',
|
|
773
|
+
pipeline_steps: steps,
|
|
774
|
+
rule_results: vr.rule_results,
|
|
775
|
+
failure_reason: vr.failure_reason,
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
return result;
|
|
779
|
+
}, [file_validation_results, file_pipeline_steps, immediate_clarifications]);
|
|
780
|
+
// Per-file validation badges for clarification doc references
|
|
781
|
+
const file_validation_badges = useMemo(() => {
|
|
782
|
+
const badges = {};
|
|
783
|
+
for (const [file_id, vr] of Object.entries(file_validation_results)) {
|
|
784
|
+
if (validating_file_ids.has(file_id)) {
|
|
785
|
+
badges[file_id] = { state: 'validating' };
|
|
786
|
+
}
|
|
787
|
+
else if (vr.errors && vr.errors.length > 0) {
|
|
788
|
+
const all_responded = vr.errors.every(e => responded_clarification_ids.has(e.id));
|
|
789
|
+
if (all_responded) {
|
|
790
|
+
badges[file_id] = { state: 'resolved', issue_count: vr.errors.length };
|
|
791
|
+
}
|
|
792
|
+
else {
|
|
793
|
+
const pending_count = responded_clarification_ids.size > 0
|
|
794
|
+
? vr.errors.filter(e => !responded_clarification_ids.has(e.id)).length
|
|
795
|
+
: vr.errors.length;
|
|
796
|
+
badges[file_id] = { state: 'issues', issue_count: pending_count };
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
else if (vr.rule_results && vr.rule_results.some(r => r.has_issue)) {
|
|
800
|
+
const issue_count = vr.rule_results.filter(r => r.has_issue).length;
|
|
801
|
+
badges[file_id] = { state: 'issues', issue_count };
|
|
802
|
+
}
|
|
803
|
+
else if (vr.status === 'passed') {
|
|
804
|
+
badges[file_id] = { state: 'passed' };
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
return Object.keys(badges).length > 0 ? badges : undefined;
|
|
808
|
+
}, [file_validation_results, validating_file_ids, responded_clarification_ids]);
|
|
809
|
+
// Pending clarifications for Action Required section (only pending items)
|
|
810
|
+
// Enriched with any pending responses so the radio selection is retained after submission
|
|
811
|
+
const pending_clarifications = useMemo(() => {
|
|
812
|
+
return sent_clarifications
|
|
813
|
+
.filter((c) => c.status === 'pending' || c.status === 'responded')
|
|
814
|
+
.map(item => {
|
|
815
|
+
const pending = pending_clarification_responses.get(item.id);
|
|
816
|
+
if (pending) {
|
|
817
|
+
return {
|
|
818
|
+
...item,
|
|
819
|
+
status: pending.response_choice ? 'responded' : item.status,
|
|
820
|
+
response_choice: pending.response_choice ?? item.response_choice,
|
|
821
|
+
user_comment: pending.user_comment ?? item.user_comment,
|
|
822
|
+
response_files: pending.response_files ?? item.response_files,
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
return item;
|
|
826
|
+
});
|
|
827
|
+
}, [sent_clarifications, pending_clarification_responses]);
|
|
828
|
+
// Sourced clarifications: items prepared into this instance from other instances.
|
|
829
|
+
// Primary source: front_form_data.__clarifications (copied during prepare_for_client).
|
|
830
|
+
// Fallback: resolve from all_clarifications by sourced_clarification_ids.
|
|
831
|
+
// Enriched with pending responses so radio selection is retained after submission.
|
|
832
|
+
const sourced_clarifications = useMemo(() => {
|
|
833
|
+
if (!is_multi_instance || !active_instance)
|
|
834
|
+
return [];
|
|
835
|
+
// Direct source: items copied into this instance's front_form_data
|
|
836
|
+
const direct_items = Array.isArray(active_instance.front_form_data?.['__clarifications'])
|
|
837
|
+
? active_instance.front_form_data['__clarifications']
|
|
838
|
+
: [];
|
|
839
|
+
let items;
|
|
840
|
+
if (direct_items.length > 0) {
|
|
841
|
+
items = direct_items;
|
|
842
|
+
}
|
|
843
|
+
else if (active_instance.sourced_clarification_ids?.length) {
|
|
844
|
+
// Fallback: resolve by sourced_clarification_ids from cross-instance aggregation
|
|
845
|
+
const sourced_ids = new Set(active_instance.sourced_clarification_ids);
|
|
846
|
+
items = all_clarifications.filter((c) => sourced_ids.has(c.id));
|
|
847
|
+
}
|
|
848
|
+
else {
|
|
849
|
+
return [];
|
|
850
|
+
}
|
|
851
|
+
// Enrich with pending responses
|
|
852
|
+
return items.map(item => {
|
|
853
|
+
const pending = pending_clarification_responses.get(item.id);
|
|
854
|
+
if (pending) {
|
|
855
|
+
return {
|
|
856
|
+
...item,
|
|
857
|
+
status: pending.response_choice ? 'responded' : item.status,
|
|
858
|
+
response_choice: pending.response_choice ?? item.response_choice,
|
|
859
|
+
user_comment: pending.user_comment ?? item.user_comment,
|
|
860
|
+
response_files: pending.response_files ?? item.response_files,
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
return item;
|
|
864
|
+
});
|
|
865
|
+
}, [is_multi_instance, active_instance, all_clarifications, pending_clarification_responses]);
|
|
866
|
+
const pending_counts = useMemo(() => {
|
|
867
|
+
const counts = { pending: 0, responded: 0, resolved: 0, dismissed: 0, total: 0 };
|
|
868
|
+
for (const item of pending_clarifications) {
|
|
869
|
+
counts[item.status]++;
|
|
870
|
+
counts.total++;
|
|
871
|
+
}
|
|
872
|
+
return counts;
|
|
873
|
+
}, [pending_clarifications]);
|
|
874
|
+
const handle_clarification_doc_click = useCallback((file_id) => {
|
|
875
|
+
// Find the attachment from clarification doc_references to reuse existing PDF viewer
|
|
876
|
+
for (const item of [...pending_clarifications, ...sourced_clarifications, ...immediate_clarifications]) {
|
|
877
|
+
const ref = item.doc_references?.find((r) => r.file_id === file_id);
|
|
878
|
+
if (ref) {
|
|
879
|
+
set_viewed_file({
|
|
880
|
+
file_id: ref.file_id,
|
|
881
|
+
file_name: ref.file_name,
|
|
882
|
+
mime_type: ref.mime_type,
|
|
883
|
+
file_size: 0,
|
|
884
|
+
ref_id: '',
|
|
885
|
+
visibility: 'public',
|
|
886
|
+
attached_at: new Date().toISOString(),
|
|
887
|
+
});
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
}, [pending_clarifications, sourced_clarifications, immediate_clarifications]);
|
|
892
|
+
// Stepper navigation
|
|
893
|
+
const go_next = useCallback(() => {
|
|
894
|
+
set_completed_steps(prev => { const s = new Set(prev); s.add(active_front_step); return s; });
|
|
895
|
+
set_active_front_step(prev => Math.min(prev + 1, 2));
|
|
896
|
+
set_viewed_file(null);
|
|
897
|
+
}, [active_front_step]);
|
|
898
|
+
const handle_step_click = useCallback((step) => {
|
|
899
|
+
// Mark all steps before the target as completed
|
|
900
|
+
set_completed_steps(prev => {
|
|
901
|
+
const s = new Set(prev);
|
|
902
|
+
for (let i = 0; i < step; i++)
|
|
903
|
+
s.add(i);
|
|
904
|
+
return s;
|
|
905
|
+
});
|
|
906
|
+
set_active_front_step(step);
|
|
907
|
+
set_viewed_file(null);
|
|
908
|
+
}, []);
|
|
909
|
+
// Stepper step definitions — badge shows only pending (unresolved) issues
|
|
910
|
+
const pending_validation_count = useMemo(() => immediate_clarifications.filter(c => c.status === 'pending').length, [immediate_clarifications]);
|
|
911
|
+
const stepper_steps = useMemo(() => [
|
|
912
|
+
{ label: front_sections[0]?.section_name || 'Income & Expenses' },
|
|
913
|
+
{ label: 'Clarifications', badge_count: pending_validation_count },
|
|
914
|
+
{ label: 'Summary & Review' },
|
|
915
|
+
], [front_sections, pending_validation_count]);
|
|
916
|
+
// Collect clarification responses locally (not flushed to back_form_data until submit)
|
|
917
|
+
// Also immediately inject response files into the file_textbox field so they appear in the form.
|
|
918
|
+
const handle_response_change = useCallback(async (id, response) => {
|
|
919
|
+
set_pending_clarification_responses(prev => {
|
|
920
|
+
const next = new Map(prev);
|
|
921
|
+
next.set(id, response);
|
|
922
|
+
return next;
|
|
923
|
+
});
|
|
924
|
+
// Reset response validation when files change
|
|
925
|
+
const prev_response = pending_clarification_responses.get(id);
|
|
926
|
+
const prev_file_count = prev_response?.response_files?.length ?? 0;
|
|
927
|
+
const new_file_count = response.response_files?.length ?? 0;
|
|
928
|
+
if (new_file_count === 0 || new_file_count !== prev_file_count) {
|
|
929
|
+
if (response_validations[id]) {
|
|
930
|
+
set_response_validations(prev => {
|
|
931
|
+
const next = { ...prev };
|
|
932
|
+
delete next[id];
|
|
933
|
+
return next;
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
// Inject response files into the file_textbox field (with optional validation gate)
|
|
938
|
+
if (response.response_files && response.response_files.length > 0 && on_front_change && field_textbox_configs) {
|
|
939
|
+
// Find the clarification item — check sent, draft, sourced, and all clarifications
|
|
940
|
+
const all_items = [...(sent_clarifications ?? []), ...(draft_clarifications ?? []), ...(all_clarifications ?? [])];
|
|
941
|
+
const clar_item = all_items.find(item => item.id === id);
|
|
942
|
+
// Check if the selected option has shows_file_upload
|
|
943
|
+
const selected_option = clar_item?.response_options?.find(o => o.value === response.response_choice);
|
|
944
|
+
const is_file_response = selected_option?.shows_file_upload === true;
|
|
945
|
+
// Run response validation if endpoints are configured
|
|
946
|
+
if (is_file_response && clar_item && props.validation_api_endpoint && props.prompts_api_endpoint) {
|
|
947
|
+
const passed = await validate_clarification_response(id, clar_item, response);
|
|
948
|
+
if (!passed)
|
|
949
|
+
return; // Don't inject files on failure
|
|
950
|
+
}
|
|
951
|
+
// File injection logic — use ref to avoid stale closure after async validation
|
|
952
|
+
const source_ref = clar_item?.doc_references?.[0];
|
|
953
|
+
const source_field_id = source_ref?.source_field_id;
|
|
954
|
+
const source_file_id = source_ref?.file_id;
|
|
955
|
+
const field_textbox_ids = Object.keys(field_textbox_configs);
|
|
956
|
+
const target_field_id = source_field_id && field_textbox_ids.includes(source_field_id)
|
|
957
|
+
? source_field_id
|
|
958
|
+
: field_textbox_ids[0];
|
|
959
|
+
if (!target_field_id)
|
|
960
|
+
return;
|
|
961
|
+
const current_form_data = front_form_data_ref.current;
|
|
962
|
+
const existing_blocks = normalize_file_textbox_value(current_form_data[target_field_id]);
|
|
963
|
+
const existing_file_ids = new Set(existing_blocks.filter((b) => b.type === 'file')
|
|
964
|
+
.map(b => b.attachment.file_id));
|
|
965
|
+
// Build new file blocks, skipping duplicates
|
|
966
|
+
const new_file_blocks = response.response_files
|
|
967
|
+
.filter(att => !existing_file_ids.has(att.file_id))
|
|
968
|
+
.map(att => ({ type: 'file', attachment: att, replaces: source_file_id }));
|
|
969
|
+
if (new_file_blocks.length === 0)
|
|
970
|
+
return;
|
|
971
|
+
// Insert after the source file if found, else append
|
|
972
|
+
const updated_blocks = [...existing_blocks];
|
|
973
|
+
if (source_file_id) {
|
|
974
|
+
const source_idx = updated_blocks.findIndex(b => b.type === 'file' && b.attachment.file_id === source_file_id);
|
|
975
|
+
if (source_idx >= 0) {
|
|
976
|
+
if (response.response_choice === 'replace_document') {
|
|
977
|
+
const original = updated_blocks[source_idx];
|
|
978
|
+
updated_blocks[source_idx] = { ...original, replaced_by: response.response_files[0].file_id };
|
|
979
|
+
}
|
|
980
|
+
updated_blocks.splice(source_idx + 1, 0, ...new_file_blocks);
|
|
981
|
+
}
|
|
982
|
+
else {
|
|
983
|
+
updated_blocks.push(...new_file_blocks);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
else {
|
|
987
|
+
updated_blocks.push(...new_file_blocks);
|
|
988
|
+
}
|
|
989
|
+
on_front_change(target_field_id, updated_blocks);
|
|
990
|
+
}
|
|
991
|
+
}, [set_pending_clarification_responses, 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]);
|
|
992
|
+
// Build PDF viewer URL
|
|
993
|
+
const viewed_file_url = useMemo(() => {
|
|
994
|
+
if (!viewed_file || !file_manager?.callbacks?.get_download_url)
|
|
995
|
+
return null;
|
|
996
|
+
return file_manager.callbacks.get_download_url(viewed_file.file_id, viewed_file.visibility);
|
|
997
|
+
}, [viewed_file, file_manager]);
|
|
998
|
+
// ── Step 0: Form fields content ──
|
|
999
|
+
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: {}, hide_submit: true, on_response_change: handle_response_change, on_doc_click: file_manager ? handle_clarification_doc_click : 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 && (_jsxs("div", { className: "border border-amber-200/60 rounded-xl bg-card overflow-hidden", children: [_jsx("div", { className: "flex items-center gap-2 px-5 py-3 border-b border-amber-100/80", children: _jsx("h3", { className: "text-sm font-semibold text-amber-800", children: "Action Required" }) }), _jsx("div", { className: "p-5", children: _jsx(ClarificationSection, { items: sourced_clarifications, counts: {
|
|
1000
|
+
pending: sourced_clarifications.filter(c => c.status === 'pending').length,
|
|
1001
|
+
responded: sourced_clarifications.filter(c => c.status === 'responded').length,
|
|
1002
|
+
resolved: sourced_clarifications.filter(c => c.status === 'resolved').length,
|
|
1003
|
+
dismissed: sourced_clarifications.filter(c => c.status === 'dismissed').length,
|
|
1004
|
+
total: sourced_clarifications.length,
|
|
1005
|
+
}, callbacks: {}, hide_submit: true, on_response_change: handle_response_change, on_doc_click: file_manager ? handle_clarification_doc_click : 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 }) })] })), 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: [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) => {
|
|
1006
|
+
const fields = group.fields || [];
|
|
1007
|
+
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 && (_jsx("h4", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: group.label })), group.label && fields.length > 0 && group.description && (_jsx("p", { className: "text-sm text-muted-foreground/70 -mt-1", children: group.description })), fields.map((field, fi) => {
|
|
1008
|
+
const available_tags = field_textbox_configs?.[field.id]?.classification.available_tags ?? props.available_tags ?? [];
|
|
1009
|
+
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, 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));
|
|
1010
|
+
}), 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));
|
|
1011
|
+
}), 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" }) }))] }));
|
|
1012
|
+
// ── Step 1: Validation Issues content ──
|
|
1013
|
+
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: {}, hide_submit: true, hide_header: true, on_response_change: is_front_read_only ? undefined : handle_response_change, on_doc_click: file_manager ? handle_clarification_doc_click : 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("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." })] })] }) })) }));
|
|
1014
|
+
// ── Step 2: Summary & Review content ──
|
|
1015
|
+
const step_2_content = (_jsx(SummaryReviewView, { on_file_view: file_manager ? handle_file_view : undefined, on_submitted: () => set_front_submitted(true) }));
|
|
1016
|
+
// ── Next button ──
|
|
1017
|
+
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: ["Next", _jsx(HiChevronRight, { className: "h-4 w-4" })] }) }));
|
|
1018
|
+
// ── Build form_content based on stepper mode ──
|
|
1019
|
+
const front_office_title = props.front_office_title;
|
|
1020
|
+
const form_content = use_stepper ? (_jsxs("div", { className: "space-y-4", children: [_jsx(FrontOfficeStepper, { steps: stepper_steps, active_step: active_front_step, completed_steps: completed_steps, on_step_click: handle_step_click }), active_front_step === 0 && (_jsxs(_Fragment, { children: [step_0_content, !is_front_read_only && next_button] })), active_front_step === 1 && (_jsxs(_Fragment, { children: [step_1_content, !is_front_read_only && next_button] })), active_front_step === 2 && step_2_content, validation_detail_file_id && (() => {
|
|
1021
|
+
const vr = file_validation_results[validation_detail_file_id];
|
|
1022
|
+
if (!vr)
|
|
1023
|
+
return null;
|
|
1024
|
+
const rule_results = vr.rule_results ?? [];
|
|
1025
|
+
const has_only_failures = rule_results.length > 0 && rule_results.every(rr => rr.has_issue);
|
|
1026
|
+
const file_clarifications = has_only_failures
|
|
1027
|
+
? immediate_clarifications.filter(item => item.doc_references?.some(r => r.file_id === validation_detail_file_id))
|
|
1028
|
+
: [];
|
|
1029
|
+
if (has_only_failures && file_clarifications.length > 0) {
|
|
1030
|
+
const file_counts = { pending: 0, responded: 0, resolved: 0, dismissed: 0, total: 0 };
|
|
1031
|
+
for (const item of file_clarifications) {
|
|
1032
|
+
file_counts[item.status]++;
|
|
1033
|
+
file_counts.total++;
|
|
1034
|
+
}
|
|
1035
|
+
return (_jsx(HazoUiDialog, { open: true, onOpenChange: (open) => { if (!open)
|
|
1036
|
+
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: {}, hide_submit: true, hide_header: true, on_response_change: is_front_read_only ? undefined : handle_response_change, on_doc_click: file_manager ? handle_clarification_doc_click : 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 }) }));
|
|
1037
|
+
}
|
|
1038
|
+
const status_label = vr.status === 'passed' ? 'Passed' : vr.status === 'skipped' ? 'Skipped' : vr.status === 'failed' ? 'Failed' : vr.status;
|
|
1039
|
+
return (_jsx(HazoUiDialog, { open: true, onOpenChange: (open) => { if (!open)
|
|
1040
|
+
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.has_issue) && (_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.has_issue).length, ")"] }), _jsx("div", { className: "mt-1.5 space-y-1.5", children: rule_results.filter(rr => rr.has_issue).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.issue_description && (_jsx("p", { className: "text-muted-foreground text-xs mt-0.5", children: rr.issue_description }))] })] }, i))) })] })), rule_results.some(rr => !rr.has_issue) && (_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.has_issue).length, ")"] }), _jsx("div", { className: "mt-1.5 space-y-1.5", children: rule_results.filter(rr => !rr.has_issue).map((rr, i) => {
|
|
1041
|
+
let explanation;
|
|
1042
|
+
if (rr.issue_description)
|
|
1043
|
+
explanation = rr.issue_description;
|
|
1044
|
+
else if (rr.raw_response) {
|
|
1045
|
+
try {
|
|
1046
|
+
const parsed = JSON.parse(rr.raw_response.replace(/```(?:json)?\s*\n?/g, '').replace(/```/g, '').trim());
|
|
1047
|
+
explanation = parsed.validation_details || parsed.issue_description || parsed.description || parsed.reason;
|
|
1048
|
+
}
|
|
1049
|
+
catch { /* ignore */ }
|
|
1050
|
+
}
|
|
1051
|
+
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));
|
|
1052
|
+
}) })] })), 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."] })) }) }));
|
|
1053
|
+
})()] })) : (
|
|
1054
|
+
/* Legacy scrollable layout (front_office_stepper={false}) */
|
|
1055
|
+
_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: {}, hide_submit: true, hide_header: true, on_response_change: handle_response_change, on_doc_click: file_manager ? handle_clarification_doc_click : 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 }) })] })), _jsx(SummaryReviewView, { on_file_view: file_manager ? handle_file_view : undefined, on_submitted: () => set_front_submitted(true) }), validation_detail_file_id && (() => {
|
|
1056
|
+
const vr = file_validation_results[validation_detail_file_id];
|
|
1057
|
+
if (!vr)
|
|
1058
|
+
return null;
|
|
1059
|
+
const rule_results = vr.rule_results ?? [];
|
|
1060
|
+
const has_only_failures = rule_results.length > 0 && rule_results.every(rr => rr.has_issue);
|
|
1061
|
+
const file_clarifications = has_only_failures
|
|
1062
|
+
? immediate_clarifications.filter(item => item.doc_references?.some(r => r.file_id === validation_detail_file_id))
|
|
1063
|
+
: [];
|
|
1064
|
+
if (has_only_failures && file_clarifications.length > 0) {
|
|
1065
|
+
const file_counts = { pending: 0, responded: 0, resolved: 0, dismissed: 0, total: 0 };
|
|
1066
|
+
for (const item of file_clarifications) {
|
|
1067
|
+
file_counts[item.status]++;
|
|
1068
|
+
file_counts.total++;
|
|
1069
|
+
}
|
|
1070
|
+
return (_jsx(HazoUiDialog, { open: true, onOpenChange: (open) => { if (!open)
|
|
1071
|
+
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: {}, hide_submit: true, hide_header: true, on_response_change: is_front_read_only ? undefined : handle_response_change, on_doc_click: file_manager ? handle_clarification_doc_click : 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 }) }));
|
|
1072
|
+
}
|
|
1073
|
+
const status_label = vr.status === 'passed' ? 'Passed' : vr.status === 'skipped' ? 'Skipped' : vr.status === 'failed' ? 'Failed' : vr.status;
|
|
1074
|
+
return (_jsx(HazoUiDialog, { open: true, onOpenChange: (open) => { if (!open)
|
|
1075
|
+
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.has_issue) && (_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.has_issue).length, ")"] }), _jsx("div", { className: "mt-1.5 space-y-1.5", children: rule_results.filter(rr => rr.has_issue).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.issue_description && (_jsx("p", { className: "text-muted-foreground text-xs mt-0.5", children: rr.issue_description }))] })] }, i))) })] })), rule_results.some(rr => !rr.has_issue) && (_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.has_issue).length, ")"] }), _jsx("div", { className: "mt-1.5 space-y-1.5", children: rule_results.filter(rr => !rr.has_issue).map((rr, i) => {
|
|
1076
|
+
let explanation;
|
|
1077
|
+
if (rr.issue_description)
|
|
1078
|
+
explanation = rr.issue_description;
|
|
1079
|
+
else if (rr.raw_response) {
|
|
1080
|
+
try {
|
|
1081
|
+
const parsed = JSON.parse(rr.raw_response.replace(/```(?:json)?\s*\n?/g, '').replace(/```/g, '').trim());
|
|
1082
|
+
explanation = parsed.validation_details || parsed.issue_description || parsed.description || parsed.reason;
|
|
1083
|
+
}
|
|
1084
|
+
catch { /* ignore */ }
|
|
1085
|
+
}
|
|
1086
|
+
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));
|
|
1087
|
+
}) })] })), 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."] })) }) }));
|
|
1088
|
+
})()] }));
|
|
1089
|
+
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;
|
|
1090
|
+
// If no file is being viewed, just show the form
|
|
1091
|
+
if (!viewed_file || !viewed_file_url) {
|
|
1092
|
+
return (_jsxs(_Fragment, { children: [title_element, form_content] }));
|
|
1093
|
+
}
|
|
1094
|
+
// Side-by-side layout: form + PDF panel
|
|
1095
|
+
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) })] }));
|
|
1096
|
+
}
|
|
1097
|
+
//# sourceMappingURL=front_office_view.js.map
|