hazo_collab_forms 3.1.6 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGE_LOG.md +207 -0
- package/README.md +3 -0
- package/dist/components/clarification/clarification_item_body.d.ts +19 -1
- package/dist/components/clarification/clarification_item_body.d.ts.map +1 -1
- package/dist/components/clarification/clarification_item_body.js +114 -6
- package/dist/components/clarification/clarification_item_body.js.map +1 -1
- package/dist/components/clarification/clarification_thread.js +1 -1
- package/dist/components/clarification/clarification_thread.js.map +1 -1
- package/dist/components/clarification/index.d.ts +2 -0
- package/dist/components/clarification/index.d.ts.map +1 -1
- package/dist/components/clarification/index.js +1 -0
- package/dist/components/clarification/index.js.map +1 -1
- package/dist/components/clarification/resolution_status_strip.d.ts +18 -0
- package/dist/components/clarification/resolution_status_strip.d.ts.map +1 -0
- package/dist/components/clarification/resolution_status_strip.js +20 -0
- package/dist/components/clarification/resolution_status_strip.js.map +1 -0
- package/dist/components/hazo_fb_form/context.d.ts +1 -1
- package/dist/components/hazo_fb_form/context.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/hazo_fb_form.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/hazo_fb_form.js +330 -113
- package/dist/components/hazo_fb_form/hazo_fb_form.js.map +1 -1
- package/dist/components/hazo_fb_form/hooks/use_fb_form_state.d.ts +3 -3
- package/dist/components/hazo_fb_form/hooks/use_fb_form_state.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/hooks/use_fb_form_state.js +340 -61
- package/dist/components/hazo_fb_form/hooks/use_fb_form_state.js.map +1 -1
- package/dist/components/hazo_fb_form/hooks/use_llm_run.d.ts +3 -1
- package/dist/components/hazo_fb_form/hooks/use_llm_run.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/hooks/use_llm_run.js +89 -11
- package/dist/components/hazo_fb_form/hooks/use_llm_run.js.map +1 -1
- package/dist/components/hazo_fb_form/shared/agent_stepper.js +1 -1
- package/dist/components/hazo_fb_form/shared/agent_stepper.js.map +1 -1
- package/dist/components/hazo_fb_form/shared/file_status_accordion.d.ts +9 -0
- package/dist/components/hazo_fb_form/shared/file_status_accordion.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/shared/file_status_accordion.js +39 -0
- package/dist/components/hazo_fb_form/shared/file_status_accordion.js.map +1 -0
- package/dist/components/hazo_fb_form/shared/format.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/shared/format.js +8 -3
- package/dist/components/hazo_fb_form/shared/format.js.map +1 -1
- package/dist/components/hazo_fb_form/shared/send_back_item_card.d.ts +7 -1
- package/dist/components/hazo_fb_form/shared/send_back_item_card.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/shared/send_back_item_card.js +6 -3
- package/dist/components/hazo_fb_form/shared/send_back_item_card.js.map +1 -1
- package/dist/components/hazo_fb_form/types.d.ts +3 -1
- package/dist/components/hazo_fb_form/types.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/views/back_office_view.js +1 -1
- package/dist/components/hazo_fb_form/views/back_office_view.js.map +1 -1
- package/dist/components/hazo_fb_form/views/clarifications_view.js +2 -2
- package/dist/components/hazo_fb_form/views/clarifications_view.js.map +1 -1
- package/dist/components/hazo_fb_form/views/front_office_view.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/views/front_office_view.js +62 -41
- package/dist/components/hazo_fb_form/views/front_office_view.js.map +1 -1
- package/dist/components/hazo_fb_form/views/interim_view.js +3 -3
- package/dist/components/hazo_fb_form/views/interim_view.js.map +1 -1
- package/dist/components/hazo_fb_form/views/review_queue_view.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/views/review_queue_view.js +22 -9
- package/dist/components/hazo_fb_form/views/review_queue_view.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 +32 -3
- package/dist/components/hazo_validation_rule_editor/components/rule_editor.js.map +1 -1
- package/dist/components/hazo_validation_rule_editor/components/variable_chain_input.d.ts +20 -0
- package/dist/components/hazo_validation_rule_editor/components/variable_chain_input.d.ts.map +1 -0
- package/dist/components/hazo_validation_rule_editor/components/variable_chain_input.js +34 -0
- package/dist/components/hazo_validation_rule_editor/components/variable_chain_input.js.map +1 -0
- 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 +15 -3
- package/dist/components/hazo_validation_rule_editor/context.js.map +1 -1
- package/dist/components/hazo_validation_rule_editor/types.d.ts +7 -1
- 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 +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +2 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/shared/document_type_editor.d.ts +31 -0
- package/dist/components/shared/document_type_editor.d.ts.map +1 -0
- package/dist/components/shared/document_type_editor.js +60 -0
- package/dist/components/shared/document_type_editor.js.map +1 -0
- package/dist/components/shared/file_bar/file_bar.d.ts +7 -1
- package/dist/components/shared/file_bar/file_bar.d.ts.map +1 -1
- package/dist/components/shared/file_bar/file_bar.js +5 -3
- package/dist/components/shared/file_bar/file_bar.js.map +1 -1
- package/dist/components/shared/file_bar/file_bar_validation_dialog.js +4 -4
- package/dist/components/shared/file_bar/file_bar_validation_dialog.js.map +1 -1
- package/dist/components/shared/file_status_icon.d.ts +23 -0
- package/dist/components/shared/file_status_icon.d.ts.map +1 -0
- package/dist/components/shared/file_status_icon.js +38 -0
- package/dist/components/shared/file_status_icon.js.map +1 -0
- package/dist/components/shared/json_data_panel/json_data_panel.d.ts +1 -1
- package/dist/components/shared/json_data_panel/json_data_panel.d.ts.map +1 -1
- package/dist/components/shared/json_data_panel/json_data_panel.js +27 -2
- package/dist/components/shared/json_data_panel/json_data_panel.js.map +1 -1
- package/dist/components/shared/rule_result_card.d.ts.map +1 -1
- package/dist/components/shared/rule_result_card.js +5 -4
- package/dist/components/shared/rule_result_card.js.map +1 -1
- package/dist/components/thread_form/components/add_question_dialog.d.ts +12 -0
- package/dist/components/thread_form/components/add_question_dialog.d.ts.map +1 -0
- package/dist/components/thread_form/components/add_question_dialog.js +36 -0
- package/dist/components/thread_form/components/add_question_dialog.js.map +1 -0
- package/dist/components/thread_form/components/agent_compose_dialog.d.ts +30 -0
- package/dist/components/thread_form/components/agent_compose_dialog.d.ts.map +1 -0
- package/dist/components/thread_form/components/agent_compose_dialog.js +45 -0
- package/dist/components/thread_form/components/agent_compose_dialog.js.map +1 -0
- package/dist/components/thread_form/components/clarification.d.ts +14 -0
- package/dist/components/thread_form/components/clarification.d.ts.map +1 -0
- package/dist/components/thread_form/components/clarification.js +12 -0
- package/dist/components/thread_form/components/clarification.js.map +1 -0
- package/dist/components/thread_form/components/collected_data_view.d.ts +15 -0
- package/dist/components/thread_form/components/collected_data_view.d.ts.map +1 -0
- package/dist/components/thread_form/components/collected_data_view.js +121 -0
- package/dist/components/thread_form/components/collected_data_view.js.map +1 -0
- package/dist/components/thread_form/components/coverage_card.d.ts +11 -0
- package/dist/components/thread_form/components/coverage_card.d.ts.map +1 -0
- package/dist/components/thread_form/components/coverage_card.js +60 -0
- package/dist/components/thread_form/components/coverage_card.js.map +1 -0
- package/dist/components/thread_form/components/file_bar.d.ts +93 -0
- package/dist/components/thread_form/components/file_bar.d.ts.map +1 -0
- package/dist/components/thread_form/components/file_bar.js +251 -0
- package/dist/components/thread_form/components/file_bar.js.map +1 -0
- package/dist/components/thread_form/components/file_info_dialog.d.ts +15 -0
- package/dist/components/thread_form/components/file_info_dialog.d.ts.map +1 -0
- package/dist/components/thread_form/components/file_info_dialog.js +64 -0
- package/dist/components/thread_form/components/file_info_dialog.js.map +1 -0
- package/dist/components/thread_form/components/issue_group_tree.d.ts +20 -0
- package/dist/components/thread_form/components/issue_group_tree.d.ts.map +1 -0
- package/dist/components/thread_form/components/issue_group_tree.js +164 -0
- package/dist/components/thread_form/components/issue_group_tree.js.map +1 -0
- package/dist/components/thread_form/components/pdf_side_panel.d.ts +20 -0
- package/dist/components/thread_form/components/pdf_side_panel.d.ts.map +1 -0
- package/dist/components/thread_form/components/pdf_side_panel.js +63 -0
- package/dist/components/thread_form/components/pdf_side_panel.js.map +1 -0
- package/dist/components/thread_form/components/rule_decision_row.d.ts +31 -0
- package/dist/components/thread_form/components/rule_decision_row.d.ts.map +1 -0
- package/dist/components/thread_form/components/rule_decision_row.js +20 -0
- package/dist/components/thread_form/components/rule_decision_row.js.map +1 -0
- package/dist/components/thread_form/components/send_back_message.d.ts +32 -0
- package/dist/components/thread_form/components/send_back_message.d.ts.map +1 -0
- package/dist/components/thread_form/components/send_back_message.js +82 -0
- package/dist/components/thread_form/components/send_back_message.js.map +1 -0
- package/dist/components/thread_form/components/shared.d.ts +54 -0
- package/dist/components/thread_form/components/shared.d.ts.map +1 -0
- package/dist/components/thread_form/components/shared.js +136 -0
- package/dist/components/thread_form/components/shared.js.map +1 -0
- package/dist/components/thread_form/components/task_card.d.ts +90 -0
- package/dist/components/thread_form/components/task_card.d.ts.map +1 -0
- package/dist/components/thread_form/components/task_card.js +63 -0
- package/dist/components/thread_form/components/task_card.js.map +1 -0
- package/dist/components/thread_form/components/text_doc_check.d.ts +15 -0
- package/dist/components/thread_form/components/text_doc_check.d.ts.map +1 -0
- package/dist/components/thread_form/components/text_doc_check.js +16 -0
- package/dist/components/thread_form/components/text_doc_check.js.map +1 -0
- package/dist/components/thread_form/components/text_extraction.d.ts +14 -0
- package/dist/components/thread_form/components/text_extraction.d.ts.map +1 -0
- package/dist/components/thread_form/components/text_extraction.js +16 -0
- package/dist/components/thread_form/components/text_extraction.js.map +1 -0
- package/dist/components/thread_form/components/thread_composer.d.ts +15 -0
- package/dist/components/thread_form/components/thread_composer.d.ts.map +1 -0
- package/dist/components/thread_form/components/thread_composer.js +93 -0
- package/dist/components/thread_form/components/thread_composer.js.map +1 -0
- package/dist/components/thread_form/components/thread_timeline.d.ts +65 -0
- package/dist/components/thread_form/components/thread_timeline.d.ts.map +1 -0
- package/dist/components/thread_form/components/thread_timeline.js +225 -0
- package/dist/components/thread_form/components/thread_timeline.js.map +1 -0
- package/dist/components/thread_form/hooks/use_file_pipeline.d.ts +126 -0
- package/dist/components/thread_form/hooks/use_file_pipeline.d.ts.map +1 -0
- package/dist/components/thread_form/hooks/use_file_pipeline.js +760 -0
- package/dist/components/thread_form/hooks/use_file_pipeline.js.map +1 -0
- package/dist/components/thread_form/hooks/use_thread_form.d.ts +36 -0
- package/dist/components/thread_form/hooks/use_thread_form.d.ts.map +1 -0
- package/dist/components/thread_form/hooks/use_thread_form.js +126 -0
- package/dist/components/thread_form/hooks/use_thread_form.js.map +1 -0
- package/dist/components/thread_form/index.d.ts +33 -0
- package/dist/components/thread_form/index.d.ts.map +1 -0
- package/dist/components/thread_form/index.js +30 -0
- package/dist/components/thread_form/index.js.map +1 -0
- package/dist/components/thread_form/sample_data.d.ts +8 -0
- package/dist/components/thread_form/sample_data.d.ts.map +1 -0
- package/dist/components/thread_form/sample_data.js +658 -0
- package/dist/components/thread_form/sample_data.js.map +1 -0
- package/dist/components/thread_form/thread_form.d.ts +7 -0
- package/dist/components/thread_form/thread_form.d.ts.map +1 -0
- package/dist/components/thread_form/thread_form.js +1385 -0
- package/dist/components/thread_form/thread_form.js.map +1 -0
- package/dist/components/thread_form/types.d.ts +402 -0
- package/dist/components/thread_form/types.d.ts.map +1 -0
- package/dist/components/thread_form/types.js +23 -0
- package/dist/components/thread_form/types.js.map +1 -0
- package/dist/components/thread_form/utils/file_decision_state.d.ts +22 -0
- package/dist/components/thread_form/utils/file_decision_state.d.ts.map +1 -0
- package/dist/components/thread_form/utils/file_decision_state.js +37 -0
- package/dist/components/thread_form/utils/file_decision_state.js.map +1 -0
- package/dist/components/thread_form/utils/merge_send_back.d.ts +13 -0
- package/dist/components/thread_form/utils/merge_send_back.d.ts.map +1 -0
- package/dist/components/thread_form/utils/merge_send_back.js +23 -0
- package/dist/components/thread_form/utils/merge_send_back.js.map +1 -0
- package/dist/lib/autofill_handler.d.ts.map +1 -1
- package/dist/lib/autofill_handler.js +5 -44
- package/dist/lib/autofill_handler.js.map +1 -1
- package/dist/lib/classification_handler.d.ts +105 -0
- package/dist/lib/classification_handler.d.ts.map +1 -0
- package/dist/lib/classification_handler.js +342 -0
- package/dist/lib/classification_handler.js.map +1 -0
- package/dist/lib/content_gate_handler.d.ts +37 -0
- package/dist/lib/content_gate_handler.d.ts.map +1 -0
- package/dist/lib/content_gate_handler.js +126 -0
- package/dist/lib/content_gate_handler.js.map +1 -0
- package/dist/lib/index.d.ts +10 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +5 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/periodic_coverage_runner.d.ts +24 -0
- package/dist/lib/periodic_coverage_runner.d.ts.map +1 -0
- package/dist/lib/periodic_coverage_runner.js +121 -0
- package/dist/lib/periodic_coverage_runner.js.map +1 -0
- package/dist/lib/resolution_handler.d.ts +150 -0
- package/dist/lib/resolution_handler.d.ts.map +1 -0
- package/dist/lib/resolution_handler.js +597 -0
- package/dist/lib/resolution_handler.js.map +1 -0
- package/dist/lib/resolve_variable.d.ts +25 -0
- package/dist/lib/resolve_variable.d.ts.map +1 -0
- package/dist/lib/resolve_variable.js +77 -0
- package/dist/lib/resolve_variable.js.map +1 -0
- package/dist/lib/validation_handler.d.ts +27 -3
- package/dist/lib/validation_handler.d.ts.map +1 -1
- package/dist/lib/validation_handler.js +338 -288
- package/dist/lib/validation_handler.js.map +1 -1
- package/dist/types/clarification.d.ts +54 -0
- package/dist/types/clarification.d.ts.map +1 -1
- package/dist/types/fb_form_data.d.ts +273 -123
- package/dist/types/fb_form_data.d.ts.map +1 -1
- package/dist/types/fb_form_data.js +44 -58
- package/dist/types/fb_form_data.js.map +1 -1
- package/dist/types/fb_form_data_v1.d.ts +250 -0
- package/dist/types/fb_form_data_v1.d.ts.map +1 -0
- package/dist/types/fb_form_data_v1.js +117 -0
- package/dist/types/fb_form_data_v1.js.map +1 -0
- package/dist/types/fb_form_instance.d.ts +1 -1
- package/dist/types/fb_form_instance.d.ts.map +1 -1
- package/dist/types/index.d.ts +5 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/validation.d.ts +134 -12
- package/dist/types/validation.d.ts.map +1 -1
- package/dist/utils/expectation_extractor.d.ts +31 -0
- package/dist/utils/expectation_extractor.d.ts.map +1 -0
- package/dist/utils/expectation_extractor.js +142 -0
- package/dist/utils/expectation_extractor.js.map +1 -0
- package/dist/utils/fb_data_adapter.d.ts +7 -2
- package/dist/utils/fb_data_adapter.d.ts.map +1 -1
- package/dist/utils/fb_data_adapter.js +58 -7
- package/dist/utils/fb_data_adapter.js.map +1 -1
- package/dist/utils/fb_data_adapter_v2.d.ts +17 -0
- package/dist/utils/fb_data_adapter_v2.d.ts.map +1 -0
- package/dist/utils/fb_data_adapter_v2.js +483 -0
- package/dist/utils/fb_data_adapter_v2.js.map +1 -0
- package/dist/utils/fb_data_helpers.d.ts +1 -1
- package/dist/utils/fb_data_helpers.d.ts.map +1 -1
- package/dist/utils/fb_data_mutations.d.ts +1 -1
- package/dist/utils/fb_data_mutations.d.ts.map +1 -1
- package/dist/utils/fb_data_mutations_v2.d.ts +46 -0
- package/dist/utils/fb_data_mutations_v2.d.ts.map +1 -0
- package/dist/utils/fb_data_mutations_v2.js +341 -0
- package/dist/utils/fb_data_mutations_v2.js.map +1 -0
- package/dist/utils/fb_data_queries.d.ts +81 -0
- package/dist/utils/fb_data_queries.d.ts.map +1 -0
- package/dist/utils/fb_data_queries.js +354 -0
- package/dist/utils/fb_data_queries.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 +6 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/issue_bucketing.d.ts +36 -0
- package/dist/utils/issue_bucketing.d.ts.map +1 -0
- package/dist/utils/issue_bucketing.js +107 -0
- package/dist/utils/issue_bucketing.js.map +1 -0
- package/dist/utils/validation_result.d.ts +32 -0
- package/dist/utils/validation_result.d.ts.map +1 -0
- package/dist/utils/validation_result.js +55 -0
- package/dist/utils/validation_result.js.map +1 -0
- package/package.json +16 -4
|
@@ -11,6 +11,8 @@ import { cn } from '../../utils/cn.js';
|
|
|
11
11
|
import { use_logger } from '../../logger/index.js';
|
|
12
12
|
import { is_fb_terminal_status } from '../../types/fb_form_instance.js';
|
|
13
13
|
import { flat_to_data } from '../../utils/fb_data_adapter.js';
|
|
14
|
+
import { flat_to_data_v2 } from '../../utils/fb_data_adapter_v2.js';
|
|
15
|
+
import { set_review_decision } from '../../utils/fb_data_mutations_v2.js';
|
|
14
16
|
import { FbFormContext } from './context.js';
|
|
15
17
|
import { use_fb_form_state } from './hooks/use_fb_form_state.js';
|
|
16
18
|
import { use_llm_run } from './hooks/use_llm_run.js';
|
|
@@ -25,6 +27,7 @@ import { register_ihelp_icon } from '../shared/ihelp_icon.js';
|
|
|
25
27
|
import { AgentStepper } from './shared/agent_stepper.js';
|
|
26
28
|
import { ReviewQueueView } from './views/review_queue_view.js';
|
|
27
29
|
import { SendBackView } from './views/send_back_view.js';
|
|
30
|
+
import { FileStatusAccordion } from './shared/file_status_accordion.js';
|
|
28
31
|
export const VIRTUAL_INSTANCE_ID = '__single__';
|
|
29
32
|
export function HazoFbForm(props) {
|
|
30
33
|
const logger = use_logger();
|
|
@@ -93,7 +96,7 @@ export function HazoFbForm(props) {
|
|
|
93
96
|
});
|
|
94
97
|
}
|
|
95
98
|
}, [props.data, props.on_data_change]);
|
|
96
|
-
const state = use_fb_form_state(effective_props, active_instance_id, active_instance, pending_clarification_responses);
|
|
99
|
+
const state = use_fb_form_state(effective_props, active_instance_id, active_instance, pending_clarification_responses, form_data_entries);
|
|
97
100
|
const { trigger_run, trigger_complete, trigger_classify_file, trigger_assign_file, route_skipped_files, trigger_backoffice_run } = use_llm_run({
|
|
98
101
|
props: effective_props,
|
|
99
102
|
update_progress: state.update_progress,
|
|
@@ -116,6 +119,7 @@ export function HazoFbForm(props) {
|
|
|
116
119
|
set_validating_file_ids: state.set_validating_file_ids,
|
|
117
120
|
skipped_file_ids: state.skipped_file_ids,
|
|
118
121
|
update_form_data,
|
|
122
|
+
form_data_entries,
|
|
119
123
|
});
|
|
120
124
|
// Restore group_autofill_log from persisted autofill activities in the data model.
|
|
121
125
|
// This runs on mount (and when form_data_entries changes) so that after a page refresh,
|
|
@@ -127,7 +131,7 @@ export function HazoFbForm(props) {
|
|
|
127
131
|
if (form_data_entries.length === 0)
|
|
128
132
|
return;
|
|
129
133
|
const autofill_activities = [];
|
|
130
|
-
// Walk all entries looking for autofill activities
|
|
134
|
+
// Walk all entries looking for autofill activities (v1 structure during migration)
|
|
131
135
|
for (const entry of form_data_entries) {
|
|
132
136
|
const walk = (items) => {
|
|
133
137
|
if (!items)
|
|
@@ -150,8 +154,10 @@ export function HazoFbForm(props) {
|
|
|
150
154
|
}
|
|
151
155
|
};
|
|
152
156
|
walk(entry.client_data);
|
|
153
|
-
|
|
154
|
-
|
|
157
|
+
// v1 compat: check for old clarification structure during migration
|
|
158
|
+
const v1_entry = entry;
|
|
159
|
+
if (v1_entry.clarification?.client_data)
|
|
160
|
+
walk(v1_entry.clarification.client_data);
|
|
155
161
|
}
|
|
156
162
|
if (autofill_activities.length > 0) {
|
|
157
163
|
autofill_restore_ran.current = true;
|
|
@@ -193,102 +199,174 @@ export function HazoFbForm(props) {
|
|
|
193
199
|
state.set_group_autofill_log(restored_log);
|
|
194
200
|
}
|
|
195
201
|
}, [form_data_entries]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
202
|
+
// Restore classification_results from form_data_entries when empty (e.g. Instance 2 after send-back).
|
|
203
|
+
// Files already classified in a previous instance have tags in the data model — reconstruct
|
|
204
|
+
// the classification results so trigger_backoffice_run can route and autofill them.
|
|
205
|
+
const classification_restore_ran = useRef(false);
|
|
206
|
+
useEffect(() => {
|
|
207
|
+
if (classification_restore_ran.current)
|
|
208
|
+
return;
|
|
209
|
+
if (state.classification_results.length > 0)
|
|
210
|
+
return;
|
|
211
|
+
if (form_data_entries.length === 0)
|
|
212
|
+
return;
|
|
213
|
+
const field_map = new Map();
|
|
214
|
+
const seen_file_ids = new Set();
|
|
215
|
+
const add_file = (field_id, pd, inherit_tags) => {
|
|
216
|
+
if (pd.processed_data_type !== 'file' || !pd.processed_data_files?.file_id)
|
|
217
|
+
return;
|
|
218
|
+
if (seen_file_ids.has(pd.processed_data_files.file_id))
|
|
219
|
+
return;
|
|
220
|
+
seen_file_ids.add(pd.processed_data_files.file_id);
|
|
221
|
+
if (!field_map.has(field_id))
|
|
222
|
+
field_map.set(field_id, { tags: new Set(), files: [] });
|
|
223
|
+
const bucket = field_map.get(field_id);
|
|
224
|
+
// Use pd tags, fall back to inherited tags from the original file in the same entry
|
|
225
|
+
const tags = (pd.tags && pd.tags.length > 0) ? pd.tags : (inherit_tags ?? []);
|
|
226
|
+
for (const t of tags)
|
|
227
|
+
bucket.tags.add(t);
|
|
228
|
+
bucket.files.push({
|
|
229
|
+
file_id: pd.processed_data_files.file_id,
|
|
230
|
+
file_name: pd.processed_data_files.file_name ?? '',
|
|
231
|
+
tags,
|
|
232
|
+
document_nature: pd.processed_data_nature?.[0],
|
|
233
|
+
document_type: pd.processed_data_category,
|
|
234
|
+
});
|
|
235
|
+
};
|
|
236
|
+
for (const entry of form_data_entries) {
|
|
237
|
+
const field_id = entry.question?.question_field_id;
|
|
238
|
+
if (!field_id)
|
|
239
|
+
continue;
|
|
240
|
+
// Collect tags from original client uploads first
|
|
241
|
+
const entry_tags = [];
|
|
242
|
+
for (const cdi of entry.client_data ?? []) {
|
|
243
|
+
const input = cdi.client_input;
|
|
244
|
+
if (!input?.processed_data)
|
|
245
|
+
continue;
|
|
246
|
+
for (const pd of input.processed_data) {
|
|
247
|
+
add_file(field_id, pd);
|
|
248
|
+
if (pd.tags)
|
|
249
|
+
for (const t of pd.tags)
|
|
250
|
+
if (!entry_tags.includes(t))
|
|
251
|
+
entry_tags.push(t);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Files from clarification responses inherit tags from the original file
|
|
255
|
+
for (const clr of entry.clarifications ?? []) {
|
|
256
|
+
for (const rd of clr.response_data ?? []) {
|
|
257
|
+
for (const rpd of rd.processed_data ?? [])
|
|
258
|
+
add_file(field_id, rpd, entry_tags);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (field_map.size > 0) {
|
|
263
|
+
classification_restore_ran.current = true;
|
|
264
|
+
const results = [];
|
|
265
|
+
for (const [field_id, { tags, files }] of field_map) {
|
|
266
|
+
results.push({ field_id, tags: Array.from(tags), file_classifications: files });
|
|
267
|
+
}
|
|
268
|
+
state.set_classification_results(results);
|
|
269
|
+
logger.info('[HazoFbForm] classification_results_restored_from_data', { count: results.length, files: results.flatMap(r => r.file_classifications.map(f => f.file_id)) });
|
|
270
|
+
}
|
|
271
|
+
}, [form_data_entries, state.classification_results.length]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
196
272
|
// Sync flat LLM results → hierarchical data model
|
|
197
273
|
// Rebuild data[] whenever classification, validation, or clarification state changes.
|
|
198
274
|
// Combines ALL instances into a single data[] array (matching the reference JSON structure).
|
|
199
275
|
// IMPORTANT: The JSON must be stable regardless of which instance is active — every instance
|
|
200
276
|
// is built from persisted data. The active instance additionally applies live enrichments.
|
|
277
|
+
// ── Build v2 data model from ALL instances (instance-independent) ──
|
|
278
|
+
// Uses the flattened adapter: validations and clarifications are direct arrays,
|
|
279
|
+
// thread captures full cross-instance conversation, no activities nesting.
|
|
280
|
+
//
|
|
281
|
+
// IMPORTANT: When the consumer provides persisted v2 data via `props.data`,
|
|
282
|
+
// we DON'T overwrite it with a fresh rebuild from instances — the v2 data
|
|
283
|
+
// is the single source of truth. We only rebuild when no v2 data exists yet
|
|
284
|
+
// (initial build) or when classification/validation results change (new pipeline data).
|
|
285
|
+
// ── Build v2 data model from ALL instances ──
|
|
286
|
+
// Always rebuilds from instances. The rebuild is the truth for structure (validations,
|
|
287
|
+
// classifications, review decisions). But client responses added via v2_add_client_response
|
|
288
|
+
// may not be in the v1 instances yet — so we merge those thread entries in.
|
|
201
289
|
useEffect(() => {
|
|
202
|
-
if (
|
|
290
|
+
if (instances.length === 0)
|
|
203
291
|
return;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
result = sent;
|
|
211
|
-
}
|
|
212
|
-
else {
|
|
213
|
-
const pending = pending_clarification_responses.get(item.id);
|
|
214
|
-
if (pending?.response_choice || pending?.user_comment) {
|
|
215
|
-
result = {
|
|
216
|
-
...item,
|
|
217
|
-
status: 'responded',
|
|
218
|
-
response_choice: pending.response_choice,
|
|
219
|
-
user_comment: pending.user_comment,
|
|
220
|
-
response_files: pending.response_files ?? [],
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
const decision = state.review_queue_decisions.get(item.id);
|
|
225
|
-
if (decision) {
|
|
226
|
-
result = { ...result, review_decision: decision === 'accept' ? 'approve' : 'skip_process' };
|
|
227
|
-
}
|
|
228
|
-
const comment = state.send_back_comments.get(item.id);
|
|
229
|
-
if (comment) {
|
|
230
|
-
result = { ...result, agent_feedback: comment };
|
|
292
|
+
const v2_data = flat_to_data_v2(instances);
|
|
293
|
+
if (v2_data.length === 0)
|
|
294
|
+
return;
|
|
295
|
+
update_form_data(prev => {
|
|
296
|
+
if (!prev || prev.length === 0) {
|
|
297
|
+
return v2_data;
|
|
231
298
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
? inst.front_form_data['__clarifications']
|
|
238
|
-
: [];
|
|
239
|
-
const clrs = [...(inst.sent_clarifications ?? [])];
|
|
240
|
-
const seen = new Set(clrs.map(c => c.id));
|
|
241
|
-
for (const c of front_clrs) {
|
|
242
|
-
if (!seen.has(c.id)) {
|
|
243
|
-
seen.add(c.id);
|
|
244
|
-
clrs.push(c);
|
|
299
|
+
// Build index of existing v2 clarifications for thread merging
|
|
300
|
+
const prev_clrs = new Map();
|
|
301
|
+
for (const entry of prev) {
|
|
302
|
+
for (const clr of entry.clarifications ?? []) {
|
|
303
|
+
prev_clrs.set(clr.clarification_id, clr);
|
|
245
304
|
}
|
|
246
305
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
306
|
+
if (prev_clrs.size === 0)
|
|
307
|
+
return v2_data;
|
|
308
|
+
// Use the FRESH rebuild as base (it has the latest structure from instances).
|
|
309
|
+
// Only supplement with thread entries from existing v2 that the rebuild is missing.
|
|
310
|
+
// This handles: v2_add_client_response added a thread entry that isn't in v1 yet.
|
|
311
|
+
const merged = v2_data.map((entry) => {
|
|
312
|
+
if (!entry.clarifications?.length)
|
|
313
|
+
return entry;
|
|
314
|
+
const merged_clrs = entry.clarifications.map((fresh_clr) => {
|
|
315
|
+
const existing = prev_clrs.get(fresh_clr.clarification_id);
|
|
316
|
+
if (!existing)
|
|
317
|
+
return fresh_clr;
|
|
318
|
+
// If existing v2 has MORE thread entries than the rebuild, the extra entries
|
|
319
|
+
// are from v2_add_client_response and need to be preserved.
|
|
320
|
+
const fresh_thread = fresh_clr.thread ?? [];
|
|
321
|
+
const existing_thread = existing.thread ?? [];
|
|
322
|
+
if (existing_thread.length > fresh_thread.length) {
|
|
323
|
+
// Find the extra thread entries (entries in existing but not in fresh)
|
|
324
|
+
const extra_entries = existing_thread.slice(fresh_thread.length);
|
|
325
|
+
// Only keep client responses (the ones added by v2_add_client_response)
|
|
326
|
+
const client_extras = extra_entries.filter((t) => t.role === 'client' && t.action === 'responded');
|
|
327
|
+
if (client_extras.length > 0) {
|
|
328
|
+
console.log('[DEBUG] v2 merge: appending', client_extras.length, 'client thread entries to', fresh_clr.clarification_id?.slice(-8), 'fresh_status:', fresh_clr.status);
|
|
329
|
+
return {
|
|
330
|
+
...fresh_clr,
|
|
331
|
+
thread: [...fresh_thread, ...client_extras],
|
|
332
|
+
status: 'responded', // Client has responded
|
|
333
|
+
review: null, // Needs fresh review
|
|
334
|
+
response_data: existing.response_data?.length > fresh_clr.response_data?.length
|
|
335
|
+
? existing.response_data : fresh_clr.response_data,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
// Preserve 'accepted' status set in the CURRENT session via set_review_decision.
|
|
340
|
+
// Only preserve if the acceptance was made AFTER the last client response
|
|
341
|
+
// (i.e., it's a current-session decision, not a stale one from a prior instance).
|
|
342
|
+
if (existing.status === 'accepted' && existing.review?.thread_ref) {
|
|
343
|
+
const merged_thread = existing_thread.length >= fresh_thread.length ? existing_thread : fresh_thread;
|
|
344
|
+
const review_idx = merged_thread.findIndex((t) => t.thread_id === existing.review.thread_ref);
|
|
345
|
+
const has_newer_response = merged_thread.some((t, i) => i > review_idx && t.role === 'client' && t.action === 'responded');
|
|
346
|
+
if (!has_newer_response) {
|
|
347
|
+
return {
|
|
348
|
+
...fresh_clr,
|
|
349
|
+
thread: merged_thread,
|
|
350
|
+
status: 'accepted',
|
|
351
|
+
review: existing.review,
|
|
352
|
+
response_data: existing.response_data?.length > (fresh_clr.response_data?.length ?? 0)
|
|
353
|
+
? existing.response_data : fresh_clr.response_data,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return fresh_clr;
|
|
271
358
|
});
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
...
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|| (synthetic.sent_clarifications?.length ?? 0) > 0;
|
|
284
|
-
if (has_data) {
|
|
285
|
-
all_data.push(...flat_to_data(synthetic, inst_num));
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
if (all_data.length > 0) {
|
|
289
|
-
update_form_data(() => all_data);
|
|
290
|
-
}
|
|
291
|
-
}, [state.classification_results, state.file_validation_results, state.sent_clarifications, state.draft_clarifications, pending_clarification_responses, state.review_queue_decisions, state.send_back_comments, instances, active_instance_id]);
|
|
359
|
+
// Preserve autofill_runs from existing data (not in v1 instances)
|
|
360
|
+
const prev_entry = prev.find((e) => e.clarifications?.some((c) => entry.clarifications?.some((fc) => fc.clarification_id === c.clarification_id)));
|
|
361
|
+
const merged_entry = { ...entry, clarifications: merged_clrs };
|
|
362
|
+
if (prev_entry?.autofill_runs?.length > 0 && !merged_entry.autofill_runs?.length) {
|
|
363
|
+
merged_entry.autofill_runs = prev_entry.autofill_runs;
|
|
364
|
+
}
|
|
365
|
+
return merged_entry;
|
|
366
|
+
});
|
|
367
|
+
return merged;
|
|
368
|
+
});
|
|
369
|
+
}, [instances]);
|
|
292
370
|
// Persist classification_results to instance when they change
|
|
293
371
|
const prev_cls_ref = useRef(state.classification_results);
|
|
294
372
|
useEffect(() => {
|
|
@@ -320,9 +398,15 @@ export function HazoFbForm(props) {
|
|
|
320
398
|
let changed = false;
|
|
321
399
|
const updated = current_sent.map(c => {
|
|
322
400
|
let result = c;
|
|
401
|
+
// Don't overwrite clarifications where client has already responded —
|
|
402
|
+
// their status has progressed beyond the review decision stage
|
|
403
|
+
if (c.status === 'responded' || c.response_choice || c.user_comment)
|
|
404
|
+
return result;
|
|
323
405
|
const decision = state.review_queue_decisions.get(c.id);
|
|
324
|
-
|
|
325
|
-
|
|
406
|
+
// Match confirm_review_decisions: accept → skip_process, send_back → approve
|
|
407
|
+
const mapped_decision = decision === 'accept' ? 'skip_process' : 'approve';
|
|
408
|
+
if (decision && result.review_decision !== mapped_decision) {
|
|
409
|
+
result = { ...result, review_decision: mapped_decision };
|
|
326
410
|
changed = true;
|
|
327
411
|
}
|
|
328
412
|
const comment = state.send_back_comments.get(c.id);
|
|
@@ -426,8 +510,23 @@ export function HazoFbForm(props) {
|
|
|
426
510
|
// Upsert: if item wasn't in sent_clarifications (e.g. validation draft), add it
|
|
427
511
|
if (!found) {
|
|
428
512
|
const draft = state.draft_clarifications.find((d) => d.id === id);
|
|
429
|
-
if (draft)
|
|
513
|
+
if (draft) {
|
|
430
514
|
updated.push({ ...draft, ...updates, updated_at: now });
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
// Fallback: search all validation errors for the item (draft may have been deduped)
|
|
518
|
+
for (const vr of Object.values(state.file_validation_results)) {
|
|
519
|
+
const err = vr.errors?.find((e) => e.id === id);
|
|
520
|
+
if (err) {
|
|
521
|
+
updated.push({ ...err, ...updates, updated_at: now });
|
|
522
|
+
break;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
// Last resort: create minimal item from updates
|
|
526
|
+
if (!updated.some((c) => c.id === id)) {
|
|
527
|
+
updated.push({ id, type: 'validation_override', status: 'pending', target_field_id: '', target_label: '', issue_description: '', doc_references: [], response_options: [], created_at: now, ...updates, updated_at: now });
|
|
528
|
+
}
|
|
529
|
+
}
|
|
431
530
|
}
|
|
432
531
|
// Also update front_form_data.__clarifications if the item lives there
|
|
433
532
|
const front_clrs = active_instance.front_form_data?.['__clarifications'];
|
|
@@ -438,6 +537,11 @@ export function HazoFbForm(props) {
|
|
|
438
537
|
__clarifications: front_clrs.map(c => c.id === id ? { ...c, ...updates, updated_at: now } : c),
|
|
439
538
|
};
|
|
440
539
|
}
|
|
540
|
+
logger.info('[HazoFbForm] update_clarification_status → on_instance_update', {
|
|
541
|
+
instance_id: active_instance_id,
|
|
542
|
+
sent_clrs_count: inst_updates.sent_clarifications?.length,
|
|
543
|
+
found_in_existing: found,
|
|
544
|
+
});
|
|
441
545
|
props.on_instance_update?.(active_instance_id, inst_updates);
|
|
442
546
|
}
|
|
443
547
|
else {
|
|
@@ -455,34 +559,122 @@ export function HazoFbForm(props) {
|
|
|
455
559
|
});
|
|
456
560
|
if (!found) {
|
|
457
561
|
const draft = state.draft_clarifications.find((d) => d.id === id);
|
|
458
|
-
if (draft)
|
|
562
|
+
if (draft) {
|
|
459
563
|
updated.push({ ...draft, ...updates, updated_at: now });
|
|
564
|
+
}
|
|
565
|
+
else {
|
|
566
|
+
for (const vr of Object.values(state.file_validation_results)) {
|
|
567
|
+
const err = vr.errors?.find((e) => e.id === id);
|
|
568
|
+
if (err) {
|
|
569
|
+
updated.push({ ...err, ...updates, updated_at: now });
|
|
570
|
+
break;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
if (!updated.some((c) => c.id === id)) {
|
|
574
|
+
updated.push({ id, type: 'validation_override', status: 'pending', target_field_id: '', target_label: '', issue_description: '', doc_references: [], response_options: [], created_at: now, ...updates, updated_at: now });
|
|
575
|
+
}
|
|
576
|
+
}
|
|
460
577
|
}
|
|
461
578
|
props.on_back_change?.('__clarifications', updated);
|
|
462
579
|
}
|
|
463
|
-
}, [props, is_multi_instance, active_instance, active_instance_id, state.draft_clarifications]);
|
|
580
|
+
}, [props, is_multi_instance, active_instance, active_instance_id, state.draft_clarifications, state.file_validation_results]);
|
|
464
581
|
const update_clarification_response = useCallback((id, response) => {
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
582
|
+
const now = new Date().toISOString();
|
|
583
|
+
// Find the base item from ALL available sources to ensure we have the full data
|
|
584
|
+
const find_base = () => {
|
|
585
|
+
// 1. Sent clarifications on instance
|
|
586
|
+
const sent = active_instance?.sent_clarifications?.find(c => c.id === id);
|
|
587
|
+
if (sent)
|
|
588
|
+
return sent;
|
|
589
|
+
// 2. Draft clarifications (in-memory)
|
|
590
|
+
const draft = state.draft_clarifications.find(d => d.id === id);
|
|
591
|
+
if (draft)
|
|
592
|
+
return draft;
|
|
593
|
+
// 3. File validation results errors
|
|
594
|
+
for (const vr of Object.values(state.file_validation_results)) {
|
|
595
|
+
const err = vr.errors?.find(e => e.id === id);
|
|
596
|
+
if (err)
|
|
597
|
+
return err;
|
|
598
|
+
}
|
|
599
|
+
// 4. Back form data clarifications
|
|
600
|
+
const back_clrs = Array.isArray(props.back_form_data?.['__clarifications'])
|
|
469
601
|
? props.back_form_data['__clarifications']
|
|
470
|
-
: []
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
602
|
+
: [];
|
|
603
|
+
const back = back_clrs.find(c => c.id === id);
|
|
604
|
+
if (back)
|
|
605
|
+
return back;
|
|
606
|
+
// 5. Front form data clarifications
|
|
607
|
+
const front_clrs = Array.isArray(active_instance?.front_form_data?.['__clarifications'])
|
|
608
|
+
? active_instance.front_form_data['__clarifications']
|
|
609
|
+
: [];
|
|
610
|
+
return front_clrs.find(c => c.id === id);
|
|
611
|
+
};
|
|
612
|
+
const base = find_base();
|
|
613
|
+
// Build thread entry for the client's response
|
|
614
|
+
const existing_thread = base?.thread ?? [];
|
|
615
|
+
const client_thread_entry = {
|
|
616
|
+
role: 'client',
|
|
617
|
+
action: 'responded',
|
|
618
|
+
response_choice: response.response_choice,
|
|
619
|
+
message: response.user_comment || undefined,
|
|
620
|
+
files: response.response_files?.map(f => ({
|
|
621
|
+
file_id: f.file_id,
|
|
622
|
+
ref_id: f.file_id,
|
|
623
|
+
file_name: f.file_name,
|
|
624
|
+
file_size: f.file_size ?? 0,
|
|
625
|
+
mime_type: f.mime_type ?? 'application/octet-stream',
|
|
626
|
+
visibility: 'public',
|
|
627
|
+
attached_at: now,
|
|
628
|
+
})),
|
|
629
|
+
timestamp: now,
|
|
630
|
+
};
|
|
631
|
+
// Only add if not already in thread (avoid duplicates from handle_sourced_respond)
|
|
632
|
+
const already_in_thread = existing_thread.some(t => t.role === 'client' && t.action === 'responded' && t.timestamp === now);
|
|
633
|
+
const updated_thread = already_in_thread ? existing_thread : [...existing_thread, client_thread_entry];
|
|
474
634
|
const updates = {
|
|
475
635
|
status: 'responded',
|
|
476
636
|
response_choice: response.response_choice,
|
|
477
637
|
user_comment: response.user_comment,
|
|
478
638
|
response_files: response.response_files ?? [],
|
|
639
|
+
updated_at: now,
|
|
640
|
+
thread: updated_thread,
|
|
479
641
|
};
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
642
|
+
const merged = base
|
|
643
|
+
? { ...base, ...updates }
|
|
644
|
+
: { id, type: 'validation_override', target_field_id: '', target_label: '', issue_description: '', doc_references: [], response_options: [], created_at: now, ...updates };
|
|
645
|
+
if (is_multi_instance && active_instance) {
|
|
646
|
+
// Upsert into sent_clarifications on the instance
|
|
647
|
+
const existing = active_instance.sent_clarifications ?? [];
|
|
648
|
+
const found = existing.some(c => c.id === id);
|
|
649
|
+
const updated_sent = found
|
|
650
|
+
? existing.map(c => c.id === id ? merged : c)
|
|
651
|
+
: [...existing, merged];
|
|
652
|
+
const inst_updates = { sent_clarifications: updated_sent };
|
|
653
|
+
// Also update front_form_data.__clarifications if needed
|
|
654
|
+
const front_clrs = active_instance.front_form_data?.['__clarifications'];
|
|
655
|
+
if (Array.isArray(front_clrs) && front_clrs.some((c) => c.id === id)) {
|
|
656
|
+
inst_updates.front_form_data = {
|
|
657
|
+
...active_instance.front_form_data,
|
|
658
|
+
__clarifications: front_clrs.map(c => c.id === id ? merged : c),
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
props.on_instance_update?.(active_instance_id, inst_updates);
|
|
662
|
+
// Note: v2 data model will be updated by the useEffect([instances]) rebuild
|
|
663
|
+
// triggered by the on_instance_update call above. The rebuild uses the
|
|
664
|
+
// updated instance data which includes the response.
|
|
665
|
+
}
|
|
666
|
+
else {
|
|
667
|
+
// Single-instance: update in back_form_data
|
|
668
|
+
const existing = Array.isArray(props.back_form_data?.['__clarifications'])
|
|
669
|
+
? props.back_form_data['__clarifications']
|
|
670
|
+
: [];
|
|
671
|
+
const found = existing.some(c => c.id === id);
|
|
672
|
+
const updated = found
|
|
673
|
+
? existing.map(c => c.id === id ? merged : c)
|
|
674
|
+
: [...existing, merged];
|
|
675
|
+
props.on_back_change?.('__clarifications', updated);
|
|
676
|
+
}
|
|
677
|
+
}, [is_multi_instance, active_instance, active_instance_id, props, state.draft_clarifications, state.file_validation_results]);
|
|
486
678
|
/** Batch-update multiple responses in a single write to avoid stale-state overwrites */
|
|
487
679
|
const batch_update_clarification_responses = useCallback((responses) => {
|
|
488
680
|
if (responses.size === 0)
|
|
@@ -716,7 +908,7 @@ export function HazoFbForm(props) {
|
|
|
716
908
|
rule_id: rule.rule_id,
|
|
717
909
|
rule_name: rule.rule_name,
|
|
718
910
|
issue_description: message,
|
|
719
|
-
validation_details: rule.issue_description ?? rule.raw_response ?? '',
|
|
911
|
+
validation_details: rule.summary ?? rule.issues[0]?.issue_description ?? rule.raw_response ?? '',
|
|
720
912
|
doc_references: [{
|
|
721
913
|
file_id: file.file_id,
|
|
722
914
|
file_name: file.file_name,
|
|
@@ -830,8 +1022,14 @@ export function HazoFbForm(props) {
|
|
|
830
1022
|
if (!data)
|
|
831
1023
|
return false;
|
|
832
1024
|
return Object.entries(data).some(([key, val]) => {
|
|
1025
|
+
// File keys count as data (client may submit files only)
|
|
1026
|
+
if (key.startsWith('__files_') || key.startsWith('__private_files_')) {
|
|
1027
|
+
if (Array.isArray(val) && val.length > 0)
|
|
1028
|
+
return true;
|
|
1029
|
+
return false;
|
|
1030
|
+
}
|
|
833
1031
|
if (key.startsWith('__'))
|
|
834
|
-
return false; // Skip internal keys
|
|
1032
|
+
return false; // Skip other internal keys
|
|
835
1033
|
if (val === undefined || val === null || val === '')
|
|
836
1034
|
return false;
|
|
837
1035
|
if (Array.isArray(val) && val.length === 0)
|
|
@@ -841,6 +1039,25 @@ export function HazoFbForm(props) {
|
|
|
841
1039
|
}, [effective_props.front_form_data]);
|
|
842
1040
|
// Send to Client button: show when callback is provided, instance is not past/terminal, and on front tab
|
|
843
1041
|
const can_send_to_client = !is_past_instance && !!props.on_send_to_client;
|
|
1042
|
+
// Wrap confirm_review_decisions to also persist accept/send_back to form_data_entries (v2 data model)
|
|
1043
|
+
const confirm_review_decisions_with_data = useCallback(() => {
|
|
1044
|
+
// Persist ALL decisions in a single update_form_data call to avoid stale-state overwrites
|
|
1045
|
+
// (controlled mode reads from props.data which doesn't change between calls)
|
|
1046
|
+
const current_user = props.current_user;
|
|
1047
|
+
const decisions = Array.from(state.review_queue_decisions.entries());
|
|
1048
|
+
if (decisions.length > 0) {
|
|
1049
|
+
update_form_data(prev => {
|
|
1050
|
+
let data = prev;
|
|
1051
|
+
for (const [item_id, decision] of decisions) {
|
|
1052
|
+
const comment = decision === 'send_back' ? state.send_back_comments.get(item_id) : undefined;
|
|
1053
|
+
data = set_review_decision(data, item_id, decision, comment, current_user?.id, current_user?.name);
|
|
1054
|
+
}
|
|
1055
|
+
return data;
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
// Then run the original confirm logic (UI state, file routing, step advance)
|
|
1059
|
+
return state.confirm_review_decisions();
|
|
1060
|
+
}, [state.review_queue_decisions, state.send_back_comments, state.confirm_review_decisions, update_form_data, props.current_user]);
|
|
844
1061
|
const ctx = {
|
|
845
1062
|
props: effective_props,
|
|
846
1063
|
active_tab: state.active_tab,
|
|
@@ -926,7 +1143,7 @@ export function HazoFbForm(props) {
|
|
|
926
1143
|
send_back_items: state.send_back_items,
|
|
927
1144
|
send_back_comments: state.send_back_comments,
|
|
928
1145
|
set_send_back_comments: state.set_send_back_comments,
|
|
929
|
-
confirm_review_decisions:
|
|
1146
|
+
confirm_review_decisions: confirm_review_decisions_with_data,
|
|
930
1147
|
// Reject & send back
|
|
931
1148
|
reject_clarification,
|
|
932
1149
|
// Override resolved validation
|
|
@@ -945,7 +1162,7 @@ export function HazoFbForm(props) {
|
|
|
945
1162
|
: 'border-transparent text-muted-foreground hover:text-foreground', props.tab_class_name), children: [props.front_tab_label ?? 'Client View', props.front_tab_badge && _jsx("span", { className: "ml-2", children: props.front_tab_badge })] }), props.agent_stepper && show_back_office ? (_jsx(AgentStepper, { steps: state.step_configs, active_step: state.active_step, on_step_click: (step) => {
|
|
946
1163
|
// Confirm review decisions when navigating away from review step
|
|
947
1164
|
if (state.active_step === 'review' && step !== 'review' && state.review_complete) {
|
|
948
|
-
const accepted_file_ids =
|
|
1165
|
+
const accepted_file_ids = confirm_review_decisions_with_data();
|
|
949
1166
|
if (accepted_file_ids.length > 0) {
|
|
950
1167
|
route_skipped_files(accepted_file_ids);
|
|
951
1168
|
props.on_review_accepted?.(accepted_file_ids);
|
|
@@ -965,7 +1182,7 @@ export function HazoFbForm(props) {
|
|
|
965
1182
|
? `${clarification_badge.reviewed} / ${clarification_badge.total}`
|
|
966
1183
|
: clarification_badge.total }))] }))] }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [can_send_to_client && state.active_tab === 'front' && (_jsx("button", { type: "button", onClick: () => props.on_send_to_client(active_instance_id), disabled: !has_front_data, className: cn('inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium rounded-md transition-colors', has_front_data
|
|
967
1184
|
? 'bg-primary text-primary-foreground hover:bg-primary/90'
|
|
968
|
-
: 'bg-muted text-muted-foreground cursor-not-allowed opacity-50'), children: "Send to Client" })), is_past_instance && (_jsx("span", { className: "text-xs text-muted-foreground bg-muted px-2 py-1 rounded", children: "Read-only" }))] })] }), _jsxs("div", { className: cn('flex-1 overflow-y-auto px-4'), children: [_jsx("div", { style: { display: state.active_tab === 'front' ? undefined : 'none' }, children: _jsx(FrontOfficeView, {}) }), state.active_tab === 'client_data' && show_back_office && (_jsxs(_Fragment, { children: [show_backoffice_run_button && !is_past_instance && (_jsx("div", { className: "flex justify-end pt-2 mb-4", children: _jsx(BackofficeRunButton, { progress: state.run_progress, on_run: trigger_backoffice_run, disabled: !has_front_data, className: props.run_button_class_name, run_button_label: props.run_button_label, classification_results: state.classification_results, unassigned_files: state.unassigned_files, group_autofill_log: state.group_autofill_log }) })), _jsx(ClientDataView, {})] })), state.active_tab === 'back' && show_back_office && (_jsxs(_Fragment, { children: [show_backoffice_run_button && !is_past_instance && (_jsx("div", { className: "flex justify-end pt-2 mb-4", children: _jsx(BackofficeRunButton, { progress: state.run_progress, on_run: trigger_backoffice_run, disabled: !has_front_data, className: props.run_button_class_name, run_button_label: props.run_button_label, classification_results: state.classification_results, unassigned_files: state.unassigned_files, group_autofill_log: state.group_autofill_log }) })), _jsx(BackOfficeView, {})] })), state.active_tab === 'clarifications' && show_back_office && _jsx(ClarificationsView, {}), state.active_tab === 'agent_stepper' && show_back_office && (_jsxs(_Fragment, { children: [state.active_step === 'review' && _jsx(ReviewQueueView, {}), state.active_step === 'tax_data' && (_jsxs(_Fragment, { children: [show_backoffice_run_button && !is_past_instance && (_jsx("div", { className: "flex justify-end pt-2 mb-4", children: _jsx(BackofficeRunButton, { progress: state.run_progress, on_run: trigger_backoffice_run, disabled: !has_front_data, className: props.run_button_class_name, run_button_label: props.run_button_label, classification_results: state.classification_results, unassigned_files: state.unassigned_files, group_autofill_log: state.group_autofill_log }) })), _jsx(BackOfficeView, {}), _jsxs("div", { className: "flex items-center justify-between border-t border-border/60 pt-4 mt-6 pb-4", children: [state.step_configs.find(s => s.id === 'review')?.enabled && (_jsx("button", { type: "button", onClick: () => state.set_active_step('review'), className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-muted-foreground hover:text-foreground transition-colors", children: "\u2190 Back to Review" })), state.send_back_items.length > 0 ? (_jsxs("button", { type: "button", onClick: () => state.set_active_step('send_back'), className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium bg-primary text-primary-foreground hover:bg-primary/90 rounded-md transition-colors", children: ["Next: Send Back (", state.send_back_items.length, ") \u2192"] })) : (_jsx("span", { className: "text-sm text-green-600 font-medium", children: "✓ All resolved" }))] })] })), state.active_step === 'send_back' && _jsx(SendBackView, {})] }))] })] }), _jsx(RunDetailsDialog, { open: details_open, on_open_change: set_details_open, progress: state.run_progress, classification_results: state.classification_results, unassigned_files: state.unassigned_files, group_autofill_log: state.group_autofill_log, show_run_log: show_debug_dialogs })] }) }));
|
|
1185
|
+
: 'bg-muted text-muted-foreground cursor-not-allowed opacity-50'), children: "Send to Client" })), is_past_instance && (_jsx("span", { className: "text-xs text-muted-foreground bg-muted px-2 py-1 rounded", children: "Read-only" }))] })] }), _jsxs("div", { className: cn('flex-1 overflow-y-auto px-4'), children: [_jsx("div", { style: { display: state.active_tab === 'front' ? undefined : 'none' }, children: _jsx(FrontOfficeView, {}) }), state.active_tab === 'client_data' && show_back_office && (_jsxs(_Fragment, { children: [show_backoffice_run_button && !is_past_instance && (_jsx("div", { className: "flex justify-end pt-2 mb-4", children: _jsx(BackofficeRunButton, { progress: state.run_progress, on_run: trigger_backoffice_run, disabled: !has_front_data, className: props.run_button_class_name, run_button_label: props.run_button_label, classification_results: state.classification_results, unassigned_files: state.unassigned_files, group_autofill_log: state.group_autofill_log }) })), _jsx(ClientDataView, {})] })), state.active_tab === 'back' && show_back_office && (_jsxs(_Fragment, { children: [show_backoffice_run_button && !is_past_instance && (_jsx("div", { className: "flex justify-end pt-2 mb-4", children: _jsx(BackofficeRunButton, { progress: state.run_progress, on_run: trigger_backoffice_run, disabled: !has_front_data, className: props.run_button_class_name, run_button_label: props.run_button_label, classification_results: state.classification_results, unassigned_files: state.unassigned_files, group_autofill_log: state.group_autofill_log }) })), _jsx(BackOfficeView, {})] })), state.active_tab === 'clarifications' && show_back_office && _jsx(ClarificationsView, {}), state.active_tab === 'agent_stepper' && show_back_office && (_jsxs(_Fragment, { children: [state.active_step === 'review' && _jsx(ReviewQueueView, {}), state.active_step === 'tax_data' && (_jsxs(_Fragment, { children: [show_backoffice_run_button && !is_past_instance && (_jsx("div", { className: "flex justify-end pt-2 mb-4", children: _jsx(BackofficeRunButton, { progress: state.run_progress, on_run: trigger_backoffice_run, disabled: !has_front_data && state.skipped_file_ids.size === 0 && state.classification_results.length === 0, className: props.run_button_class_name, run_button_label: props.run_button_label, classification_results: state.classification_results, unassigned_files: state.unassigned_files, group_autofill_log: state.group_autofill_log }) })), _jsx(FileStatusAccordion, {}), _jsx(BackOfficeView, {}), _jsxs("div", { className: "flex items-center justify-between border-t border-border/60 pt-4 mt-6 pb-4", children: [state.step_configs.find(s => s.id === 'review')?.enabled && (_jsx("button", { type: "button", onClick: () => state.set_active_step('review'), className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-muted-foreground hover:text-foreground transition-colors", children: "\u2190 Back to Review" })), state.send_back_items.length > 0 ? (_jsxs("button", { type: "button", onClick: () => state.set_active_step('send_back'), className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium bg-primary text-primary-foreground hover:bg-primary/90 rounded-md transition-colors", children: ["Next: Send Back (", state.send_back_items.length, ") \u2192"] })) : (_jsx("span", { className: "text-sm text-green-600 font-medium", children: "✓ All resolved" }))] })] })), state.active_step === 'send_back' && _jsx(SendBackView, {})] }))] })] }), _jsx(RunDetailsDialog, { open: details_open, on_open_change: set_details_open, progress: state.run_progress, classification_results: state.classification_results, unassigned_files: state.unassigned_files, group_autofill_log: state.group_autofill_log, show_run_log: show_debug_dialogs })] }) }));
|
|
969
1186
|
}
|
|
970
1187
|
HazoFbForm.displayName = 'HazoFbForm';
|
|
971
1188
|
//# sourceMappingURL=hazo_fb_form.js.map
|