hazo_collab_forms 3.0.13 → 3.0.17
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 +58 -0
- package/README.md +5 -38
- package/dist/components/clarification/clarification_card.d.ts +2 -0
- package/dist/components/clarification/clarification_card.d.ts.map +1 -1
- package/dist/components/clarification/clarification_card.js +13 -4
- package/dist/components/clarification/clarification_card.js.map +1 -1
- package/dist/components/clarification/clarification_doc_reference.js +1 -1
- package/dist/components/clarification/clarification_doc_reference.js.map +1 -1
- package/dist/components/clarification/clarification_group_card.d.ts +2 -0
- package/dist/components/clarification/clarification_group_card.d.ts.map +1 -1
- package/dist/components/clarification/clarification_group_card.js +2 -2
- package/dist/components/clarification/clarification_group_card.js.map +1 -1
- package/dist/components/clarification/clarification_item_body.d.ts +2 -0
- package/dist/components/clarification/clarification_item_body.d.ts.map +1 -1
- package/dist/components/clarification/clarification_item_body.js +7 -2
- package/dist/components/clarification/clarification_item_body.js.map +1 -1
- package/dist/components/clarification/clarification_response_form.d.ts +2 -0
- package/dist/components/clarification/clarification_response_form.d.ts.map +1 -1
- package/dist/components/clarification/clarification_response_form.js +2 -2
- package/dist/components/clarification/clarification_response_form.js.map +1 -1
- package/dist/components/clarification/clarification_section.d.ts +2 -0
- package/dist/components/clarification/clarification_section.d.ts.map +1 -1
- package/dist/components/clarification/clarification_section.js +2 -2
- package/dist/components/clarification/clarification_section.js.map +1 -1
- package/dist/components/clarification/clarification_thread.d.ts +19 -0
- package/dist/components/clarification/clarification_thread.d.ts.map +1 -0
- package/dist/components/clarification/clarification_thread.js +94 -0
- package/dist/components/clarification/clarification_thread.js.map +1 -0
- package/dist/components/collab_form_file_upload.js +1 -2
- package/dist/components/collab_form_file_upload.js.map +1 -1
- package/dist/components/data_ok_multi_state.d.ts.map +1 -1
- package/dist/components/data_ok_multi_state.js +1 -2
- package/dist/components/data_ok_multi_state.js.map +1 -1
- package/dist/components/hazo_collab_form_base.d.ts.map +1 -1
- package/dist/components/hazo_collab_form_base.js +6 -8
- package/dist/components/hazo_collab_form_base.js.map +1 -1
- package/dist/components/hazo_collab_form_combo.d.ts.map +1 -1
- package/dist/components/hazo_collab_form_combo.js +8 -10
- package/dist/components/hazo_collab_form_combo.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 +7 -4
- package/dist/components/hazo_collab_form_data_table.js.map +1 -1
- package/dist/components/hazo_collab_form_date.d.ts.map +1 -1
- package/dist/components/hazo_collab_form_date.js +3 -6
- package/dist/components/hazo_collab_form_date.js.map +1 -1
- package/dist/components/hazo_collab_form_file_textbox/validation_dialog.d.ts.map +1 -1
- package/dist/components/hazo_collab_form_file_textbox/validation_dialog.js +1 -1
- package/dist/components/hazo_collab_form_file_textbox/validation_dialog.js.map +1 -1
- package/dist/components/hazo_fb_form/components/draft_clarification_card.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/components/draft_clarification_card.js +19 -11
- package/dist/components/hazo_fb_form/components/draft_clarification_card.js.map +1 -1
- package/dist/components/hazo_fb_form/components/reject_clarification_dialog.d.ts +15 -0
- package/dist/components/hazo_fb_form/components/reject_clarification_dialog.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/components/reject_clarification_dialog.js +26 -0
- package/dist/components/hazo_fb_form/components/reject_clarification_dialog.js.map +1 -0
- package/dist/components/hazo_fb_form/components/run_details_dialog.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/components/run_details_dialog.js +4 -4
- package/dist/components/hazo_fb_form/components/run_details_dialog.js.map +1 -1
- package/dist/components/hazo_fb_form/components/sent_clarification_group.d.ts +2 -1
- package/dist/components/hazo_fb_form/components/sent_clarification_group.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/components/sent_clarification_group.js +2 -2
- package/dist/components/hazo_fb_form/components/sent_clarification_group.js.map +1 -1
- package/dist/components/hazo_fb_form/context.d.ts +39 -2
- package/dist/components/hazo_fb_form/context.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/context.js.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 +272 -45
- 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 +14 -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 +293 -5
- 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.map +1 -1
- package/dist/components/hazo_fb_form/hooks/use_llm_run.js +252 -50
- package/dist/components/hazo_fb_form/hooks/use_llm_run.js.map +1 -1
- package/dist/components/hazo_fb_form/index.d.ts +5 -0
- package/dist/components/hazo_fb_form/index.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/index.js +3 -0
- package/dist/components/hazo_fb_form/index.js.map +1 -1
- package/dist/components/hazo_fb_form/shared/agent_stepper.d.ts +9 -0
- package/dist/components/hazo_fb_form/shared/agent_stepper.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/shared/agent_stepper.js +17 -0
- package/dist/components/hazo_fb_form/shared/agent_stepper.js.map +1 -0
- package/dist/components/hazo_fb_form/shared/format.d.ts +3 -0
- package/dist/components/hazo_fb_form/shared/format.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/shared/format.js +16 -0
- package/dist/components/hazo_fb_form/shared/format.js.map +1 -1
- package/dist/components/hazo_fb_form/shared/group_debug_icon.d.ts +15 -0
- package/dist/components/hazo_fb_form/shared/group_debug_icon.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/shared/group_debug_icon.js +48 -0
- package/dist/components/hazo_fb_form/shared/group_debug_icon.js.map +1 -0
- package/dist/components/hazo_fb_form/shared/pdf_side_panel.js +1 -1
- package/dist/components/hazo_fb_form/shared/pdf_side_panel.js.map +1 -1
- package/dist/components/hazo_fb_form/shared/send_back_item_card.d.ts +31 -0
- package/dist/components/hazo_fb_form/shared/send_back_item_card.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/shared/send_back_item_card.js +41 -0
- package/dist/components/hazo_fb_form/shared/send_back_item_card.js.map +1 -0
- package/dist/components/hazo_fb_form/types.d.ts +47 -4
- package/dist/components/hazo_fb_form/types.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/views/back_office_view.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/views/back_office_view.js +69 -18
- package/dist/components/hazo_fb_form/views/back_office_view.js.map +1 -1
- package/dist/components/hazo_fb_form/views/clarifications_view.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/views/clarifications_view.js +18 -7
- package/dist/components/hazo_fb_form/views/clarifications_view.js.map +1 -1
- package/dist/components/hazo_fb_form/views/client_data_view.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/views/client_data_view.js +5 -2
- package/dist/components/hazo_fb_form/views/client_data_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 +121 -41
- package/dist/components/hazo_fb_form/views/front_office_view.js.map +1 -1
- package/dist/components/hazo_fb_form/views/interim_view.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/views/interim_view.js +174 -55
- 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 +14 -0
- package/dist/components/hazo_fb_form/views/review_queue_view.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/views/review_queue_view.js +162 -0
- package/dist/components/hazo_fb_form/views/review_queue_view.js.map +1 -0
- package/dist/components/hazo_fb_form/views/send_back_view.d.ts +13 -0
- package/dist/components/hazo_fb_form/views/send_back_view.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/views/send_back_view.js +203 -0
- package/dist/components/hazo_fb_form/views/send_back_view.js.map +1 -0
- package/dist/components/hazo_fb_form/views/summary_review_view.d.ts +3 -1
- package/dist/components/hazo_fb_form/views/summary_review_view.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/views/summary_review_view.js +75 -14
- package/dist/components/hazo_fb_form/views/summary_review_view.js.map +1 -1
- package/dist/components/hazo_field_selector_dialog/components/field_autocomplete.d.ts.map +1 -1
- package/dist/components/hazo_field_selector_dialog/components/field_autocomplete.js +7 -8
- package/dist/components/hazo_field_selector_dialog/components/field_autocomplete.js.map +1 -1
- package/dist/components/hazo_field_selector_dialog/components/sortable_field_item.d.ts.map +1 -1
- package/dist/components/hazo_field_selector_dialog/components/sortable_field_item.js +1 -2
- package/dist/components/hazo_field_selector_dialog/components/sortable_field_item.js.map +1 -1
- package/dist/components/hazo_template_generator/types.d.ts.map +1 -1
- package/dist/components/hazo_template_generator/types.js +1 -0
- package/dist/components/hazo_template_generator/types.js.map +1 -1
- package/dist/components/hazo_validation_rule_editor/components/rule_editor.js +1 -1
- package/dist/components/hazo_validation_rule_editor/components/rule_editor.js.map +1 -1
- package/dist/components/index.d.ts +2 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -1
- package/dist/components/index.js.map +1 -1
- package/dist/components/multi_state_radio.d.ts.map +1 -1
- package/dist/components/multi_state_radio.js +1 -2
- package/dist/components/multi_state_radio.js.map +1 -1
- package/dist/components/shared/field_tooltip/field_tooltip.d.ts.map +1 -1
- package/dist/components/shared/field_tooltip/field_tooltip.js +1 -2
- package/dist/components/shared/field_tooltip/field_tooltip.js.map +1 -1
- package/dist/components/shared/file_bar/file_bar.d.ts +12 -2
- package/dist/components/shared/file_bar/file_bar.d.ts.map +1 -1
- package/dist/components/shared/file_bar/file_bar.js +8 -9
- package/dist/components/shared/file_bar/file_bar.js.map +1 -1
- package/dist/components/shared/file_bar/file_bar_validation.d.ts +1 -1
- package/dist/components/shared/file_bar/file_bar_validation.d.ts.map +1 -1
- package/dist/components/shared/file_bar/file_bar_validation.js +9 -0
- package/dist/components/shared/file_bar/file_bar_validation.js.map +1 -1
- package/dist/components/shared/file_bar/file_bar_validation_dialog.d.ts +16 -1
- package/dist/components/shared/file_bar/file_bar_validation_dialog.d.ts.map +1 -1
- package/dist/components/shared/file_bar/file_bar_validation_dialog.js +31 -26
- package/dist/components/shared/file_bar/file_bar_validation_dialog.js.map +1 -1
- package/dist/components/shared/file_bar/file_validation_issues_dialog.d.ts +3 -1
- package/dist/components/shared/file_bar/file_validation_issues_dialog.d.ts.map +1 -1
- package/dist/components/shared/file_bar/file_validation_issues_dialog.js +2 -2
- package/dist/components/shared/file_bar/file_validation_issues_dialog.js.map +1 -1
- package/dist/components/shared/role_utils/role_utils.d.ts +1 -0
- package/dist/components/shared/role_utils/role_utils.d.ts.map +1 -1
- package/dist/components/shared/role_utils/role_utils.js +3 -0
- package/dist/components/shared/role_utils/role_utils.js.map +1 -1
- package/dist/components/shared/rule_result_card.d.ts +31 -0
- package/dist/components/shared/rule_result_card.d.ts.map +1 -0
- package/dist/components/shared/rule_result_card.js +72 -0
- package/dist/components/shared/rule_result_card.js.map +1 -0
- package/dist/components/shared/unified_field_controls/constants.d.ts.map +1 -1
- package/dist/components/shared/unified_field_controls/constants.js +1 -0
- package/dist/components/shared/unified_field_controls/constants.js.map +1 -1
- package/dist/components/shared/unified_field_controls/resolve_field_controls.d.ts +2 -0
- package/dist/components/shared/unified_field_controls/resolve_field_controls.d.ts.map +1 -1
- package/dist/components/shared/unified_field_controls/resolve_field_controls.js +4 -0
- package/dist/components/shared/unified_field_controls/resolve_field_controls.js.map +1 -1
- package/dist/components/shared/unified_field_controls/types.d.ts +5 -0
- package/dist/components/shared/unified_field_controls/types.d.ts.map +1 -1
- package/dist/components/shared/unified_field_controls/unified_field_controls.d.ts.map +1 -1
- package/dist/components/shared/unified_field_controls/unified_field_controls.js +13 -2
- package/dist/components/shared/unified_field_controls/unified_field_controls.js.map +1 -1
- package/dist/components/shared/use_base_form_field.d.ts +2 -0
- package/dist/components/shared/use_base_form_field.d.ts.map +1 -1
- package/dist/components/shared/use_base_form_field.js +4 -0
- package/dist/components/shared/use_base_form_field.js.map +1 -1
- package/dist/lib/autofill_handler.d.ts +5 -0
- package/dist/lib/autofill_handler.d.ts.map +1 -1
- package/dist/lib/autofill_handler.js +27 -10
- package/dist/lib/autofill_handler.js.map +1 -1
- package/dist/lib/fb_form_handler.d.ts.map +1 -1
- package/dist/lib/fb_form_handler.js +45 -0
- package/dist/lib/fb_form_handler.js.map +1 -1
- package/dist/types/clarification.d.ts +22 -1
- package/dist/types/clarification.d.ts.map +1 -1
- package/dist/types/icons_behaviour.d.ts +1 -1
- package/dist/types/icons_behaviour.d.ts.map +1 -1
- package/dist/types/icons_behaviour.js +3 -1
- package/dist/types/icons_behaviour.js.map +1 -1
- package/dist/types/validation.d.ts +6 -0
- package/dist/types/validation.d.ts.map +1 -1
- package/package.json +4 -8
|
@@ -44,7 +44,7 @@ function build_tag_group_map(back_sections) {
|
|
|
44
44
|
}
|
|
45
45
|
/** Route classified files to back-office groups and trigger autofill */
|
|
46
46
|
async function route_files_to_back_office(options) {
|
|
47
|
-
const { classifications, back_sections, front_form_data, on_back_change, back_form_data, autofill_api_endpoint, file_manager, update_progress, set_group_autofill_log, run_log, tracker, on_autofill_file, force_autofill } = options;
|
|
47
|
+
const { classifications, back_sections, front_form_data, on_back_change, back_form_data, autofill_api_endpoint, file_manager, update_progress, set_group_autofill_log, run_log, tracker, on_autofill_file, on_queue_files, force_autofill } = options;
|
|
48
48
|
const tag_group_map = build_tag_group_map(back_sections);
|
|
49
49
|
const errors = [];
|
|
50
50
|
const unassigned = [];
|
|
@@ -54,7 +54,17 @@ async function route_files_to_back_office(options) {
|
|
|
54
54
|
const field_files = get_files_from_value(front_form_data[cls.field_id]);
|
|
55
55
|
for (const fc of cls.file_classifications) {
|
|
56
56
|
const file_info = field_files.find((f) => f.file_id === fc.file_id);
|
|
57
|
-
|
|
57
|
+
// Build attachment from classification data if not found in front_form_data
|
|
58
|
+
const attachment = file_info?.attachment ?? {
|
|
59
|
+
file_id: fc.file_id,
|
|
60
|
+
ref_id: `ref_${fc.file_id}`,
|
|
61
|
+
file_name: fc.file_name,
|
|
62
|
+
file_size: 0,
|
|
63
|
+
mime_type: 'application/octet-stream',
|
|
64
|
+
visibility: 'public',
|
|
65
|
+
attached_at: new Date().toISOString(),
|
|
66
|
+
};
|
|
67
|
+
all_files.push({ ...fc, source_field_id: cls.field_id, attachment });
|
|
58
68
|
}
|
|
59
69
|
}
|
|
60
70
|
// Track accumulated files per group locally to avoid stale state reads.
|
|
@@ -134,10 +144,17 @@ async function route_files_to_back_office(options) {
|
|
|
134
144
|
let autofill_done = 0;
|
|
135
145
|
// Count total NEW files across all groups for progress
|
|
136
146
|
let total_autofills = 0;
|
|
147
|
+
const all_autofill_file_ids = [];
|
|
137
148
|
for (const gid of groups_to_autofill) {
|
|
138
149
|
const fk = `__files_${gid}`;
|
|
139
150
|
const new_files = newly_routed_files.get(fk) ?? [];
|
|
140
151
|
total_autofills += Math.max(new_files.length, 1);
|
|
152
|
+
for (const f of new_files)
|
|
153
|
+
all_autofill_file_ids.push(f.file_id);
|
|
154
|
+
}
|
|
155
|
+
// Mark all files as queued before autofill starts
|
|
156
|
+
if (all_autofill_file_ids.length > 0) {
|
|
157
|
+
on_queue_files?.(all_autofill_file_ids, true);
|
|
141
158
|
}
|
|
142
159
|
// Add autofill actions to the pipeline tracker (now that we know the count)
|
|
143
160
|
if (tracker) {
|
|
@@ -180,6 +197,7 @@ async function route_files_to_back_office(options) {
|
|
|
180
197
|
current_step: `${pct}Auto-filling ${group_id} — file ${fi + 1}/${group_files.length} (${autofill_done}/${total_autofills})`,
|
|
181
198
|
overall_percent: tracker ? Math.round((tracker.completed / tracker.total) * 100) : undefined,
|
|
182
199
|
});
|
|
200
|
+
on_queue_files?.([f.file_id], false);
|
|
183
201
|
on_autofill_file?.(f.file_id, true);
|
|
184
202
|
try {
|
|
185
203
|
const download_url = file_manager?.callbacks?.get_download_url?.(f.file_id, 'public') ?? '';
|
|
@@ -265,6 +283,8 @@ async function route_files_to_back_office(options) {
|
|
|
265
283
|
on_autofill_file?.(f.file_id, false);
|
|
266
284
|
}
|
|
267
285
|
}
|
|
286
|
+
// Collect run_log entries for this group
|
|
287
|
+
const group_run_log = run_log.filter((entry) => entry.step === 'autofill' && entry.label.startsWith(`Autofill: ${group_id}`));
|
|
268
288
|
// Set autofill log for this group
|
|
269
289
|
if (group_error && group_fields_populated === 0) {
|
|
270
290
|
// If there was an error and no fields were populated, check if it's a real error or just empty
|
|
@@ -275,6 +295,7 @@ async function route_files_to_back_office(options) {
|
|
|
275
295
|
status: is_empty ? 'empty' : 'error',
|
|
276
296
|
message: group_error,
|
|
277
297
|
timestamp: Date.now(),
|
|
298
|
+
run_log: group_run_log.length > 0 ? group_run_log : undefined,
|
|
278
299
|
},
|
|
279
300
|
}));
|
|
280
301
|
}
|
|
@@ -287,6 +308,7 @@ async function route_files_to_back_office(options) {
|
|
|
287
308
|
message: `Populated ${group_fields_populated} field(s) from ${file_label}`,
|
|
288
309
|
timestamp: Date.now(),
|
|
289
310
|
details,
|
|
311
|
+
run_log: group_run_log.length > 0 ? group_run_log : undefined,
|
|
290
312
|
},
|
|
291
313
|
}));
|
|
292
314
|
}
|
|
@@ -297,6 +319,7 @@ async function route_files_to_back_office(options) {
|
|
|
297
319
|
status: 'empty',
|
|
298
320
|
message: 'No matching data found in document',
|
|
299
321
|
timestamp: Date.now(),
|
|
322
|
+
run_log: group_run_log.length > 0 ? group_run_log : undefined,
|
|
300
323
|
},
|
|
301
324
|
}));
|
|
302
325
|
}
|
|
@@ -384,9 +407,11 @@ async function validate_classified_files(options) {
|
|
|
384
407
|
const check_type_filter = options.check_type_filter;
|
|
385
408
|
// check_type filter: rules without check_type default to 'immediate' context.
|
|
386
409
|
// When filtering for 'backoffice', only explicitly backoffice rules run.
|
|
410
|
+
// check_type filter: rules without check_type run in ALL contexts (immediate + backoffice).
|
|
411
|
+
// Only rules with an explicit check_type are restricted to that context.
|
|
387
412
|
const matching_rules = validation_rules.filter((r) => r.enabled && (file.document_types.includes(r.document_type) ||
|
|
388
413
|
r.document_type === 'general' ||
|
|
389
|
-
file.document_types.length === 0) && (!check_type_filter || r.check_type
|
|
414
|
+
file.document_types.length === 0) && (!check_type_filter || !r.check_type || r.check_type === check_type_filter));
|
|
390
415
|
if (matching_rules.length === 0) {
|
|
391
416
|
// No rules match — preserve existing result if available (e.g. immediate passed),
|
|
392
417
|
// otherwise mark as skipped
|
|
@@ -1140,13 +1165,14 @@ export function use_llm_run({ props, update_progress, classification_results, se
|
|
|
1140
1165
|
}
|
|
1141
1166
|
}
|
|
1142
1167
|
}
|
|
1143
|
-
// Determine eligible files: passed/skipped
|
|
1168
|
+
// Determine eligible files: passed/skipped/pending/validating, OR failed but all clarifications resolved
|
|
1144
1169
|
const eligible_classifications = [];
|
|
1145
1170
|
for (const cls of classification_results) {
|
|
1146
1171
|
const eligible_file_cls = [];
|
|
1147
1172
|
for (const fc of cls.file_classifications) {
|
|
1148
1173
|
const vr = file_validation_results[fc.file_id];
|
|
1149
|
-
if (!vr || vr.status === 'passed' || vr.status === 'skipped') {
|
|
1174
|
+
if (!vr || vr.status === 'passed' || vr.status === 'skipped' || vr.status === 'pending' || vr.status === 'validating' || vr.status === 'manual_review') {
|
|
1175
|
+
// No validation, passed, skipped, manual_review, or validation not yet complete — eligible
|
|
1150
1176
|
eligible_file_cls.push(fc);
|
|
1151
1177
|
}
|
|
1152
1178
|
else if (vr.status === 'failed') {
|
|
@@ -1161,8 +1187,17 @@ export function use_llm_run({ props, update_progress, classification_results, se
|
|
|
1161
1187
|
if (all_resolved) {
|
|
1162
1188
|
eligible_file_cls.push(fc);
|
|
1163
1189
|
}
|
|
1190
|
+
else {
|
|
1191
|
+
logger.warn('[use_llm_run] file ineligible: failed validation with unresolved errors', { file_id: fc.file_id, file_name: fc.file_name, error_count: vr.errors.length });
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
else if (vr.status === 'manual_review') {
|
|
1195
|
+
// Client requested manual review — still eligible for routing/autofill
|
|
1196
|
+
eligible_file_cls.push(fc);
|
|
1197
|
+
}
|
|
1198
|
+
else {
|
|
1199
|
+
logger.warn('[use_llm_run] file ineligible: unexpected validation status', { file_id: fc.file_id, status: vr.status });
|
|
1164
1200
|
}
|
|
1165
|
-
// manual_review and other statuses: not eligible unless explicitly skipped
|
|
1166
1201
|
}
|
|
1167
1202
|
if (eligible_file_cls.length > 0) {
|
|
1168
1203
|
eligible_classifications.push({
|
|
@@ -1233,6 +1268,12 @@ export function use_llm_run({ props, update_progress, classification_results, se
|
|
|
1233
1268
|
set_group_autofill_log,
|
|
1234
1269
|
run_log,
|
|
1235
1270
|
on_autofill_file: handle_autofill_file,
|
|
1271
|
+
on_queue_files: (file_ids, queued) => {
|
|
1272
|
+
if (queued)
|
|
1273
|
+
add_queued_files(file_ids);
|
|
1274
|
+
else
|
|
1275
|
+
remove_queued_files(file_ids);
|
|
1276
|
+
},
|
|
1236
1277
|
});
|
|
1237
1278
|
set_unassigned_files(routing_result.unassigned);
|
|
1238
1279
|
logger.info('[use_llm_run] trigger_complete_done', {
|
|
@@ -1262,39 +1303,86 @@ export function use_llm_run({ props, update_progress, classification_results, se
|
|
|
1262
1303
|
classification_count: classification_results.length,
|
|
1263
1304
|
has_validation: !!(validation_api_endpoint && validation_rules?.length),
|
|
1264
1305
|
rule_count: validation_rules?.length ?? 0,
|
|
1306
|
+
has_on_back_change: !!on_back_change,
|
|
1307
|
+
has_autofill_endpoint: !!autofill_api_endpoint,
|
|
1308
|
+
back_sections_count: back_sections.length,
|
|
1309
|
+
back_section_groups: back_sections.map(s => (s.groups ?? []).map((g) => ({ id: g.id, tag_id: g.tag_id, prompt_area: g.prompt_area, prompt_key: g.prompt_key }))),
|
|
1265
1310
|
});
|
|
1266
|
-
if (!on_back_change)
|
|
1311
|
+
if (!on_back_change) {
|
|
1312
|
+
logger.warn('[use_llm_run] trigger_backoffice_run: no on_back_change — aborting');
|
|
1267
1313
|
return;
|
|
1268
|
-
|
|
1314
|
+
}
|
|
1315
|
+
if (classification_results.length === 0) {
|
|
1316
|
+
logger.warn('[use_llm_run] trigger_backoffice_run: no classification_results — aborting');
|
|
1269
1317
|
return;
|
|
1318
|
+
}
|
|
1270
1319
|
set_group_autofill_log({});
|
|
1271
1320
|
const run_log = [];
|
|
1272
1321
|
const errors = [];
|
|
1322
|
+
// ── Queue all classified files as "queued" so UI shows pending state ──
|
|
1323
|
+
const all_classified_file_ids = classification_results.flatMap(c => c.file_classifications.map(fc => fc.file_id));
|
|
1324
|
+
if (all_classified_file_ids.length > 0) {
|
|
1325
|
+
add_queued_files(all_classified_file_ids);
|
|
1326
|
+
}
|
|
1327
|
+
// ── Snapshot refs BEFORE backoffice validation ──
|
|
1328
|
+
// Backoffice validation triggers set_file_validation_results → useEffect clears/replaces
|
|
1329
|
+
// draft_clarifications via React state flush during await. Capture stable copies now.
|
|
1330
|
+
const snapshot_sent = [...sent_clarifications_ref.current];
|
|
1331
|
+
const snapshot_pending = new Map(pending_responses_ref.current);
|
|
1332
|
+
const snapshot_drafts = [...draft_clarifications_ref.current];
|
|
1333
|
+
// Build unified clarification items view from the snapshot
|
|
1334
|
+
const all_clarification_items = new Map();
|
|
1335
|
+
for (const item of snapshot_drafts)
|
|
1336
|
+
all_clarification_items.set(item.id, item);
|
|
1337
|
+
for (const item of snapshot_sent)
|
|
1338
|
+
all_clarification_items.set(item.id, item);
|
|
1339
|
+
for (const [clar_id, response] of snapshot_pending) {
|
|
1340
|
+
const existing = all_clarification_items.get(clar_id);
|
|
1341
|
+
if (existing && response.response_choice) {
|
|
1342
|
+
all_clarification_items.set(clar_id, {
|
|
1343
|
+
...existing,
|
|
1344
|
+
status: 'responded',
|
|
1345
|
+
response_choice: response.response_choice,
|
|
1346
|
+
user_comment: response.user_comment,
|
|
1347
|
+
response_files: response.response_files ?? existing.response_files ?? [],
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
// Build resolved rules map from snapshot (for backoffice validation to skip already-resolved rules)
|
|
1352
|
+
let effective_clarifications_for_rules = snapshot_sent;
|
|
1353
|
+
if (snapshot_pending.size > 0) {
|
|
1354
|
+
const sent_responded_ids = new Set(snapshot_sent.filter(c => c.status === 'responded' || c.status === 'resolved').map(c => c.id));
|
|
1355
|
+
const pending_items = [];
|
|
1356
|
+
for (const [clar_id, response] of snapshot_pending) {
|
|
1357
|
+
if (!response.response_choice)
|
|
1358
|
+
continue;
|
|
1359
|
+
if (sent_responded_ids.has(clar_id))
|
|
1360
|
+
continue;
|
|
1361
|
+
const original = all_clarification_items.get(clar_id);
|
|
1362
|
+
if (original) {
|
|
1363
|
+
pending_items.push({ ...original, status: 'responded', response_choice: response.response_choice, user_comment: response.user_comment });
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
if (pending_items.length > 0) {
|
|
1367
|
+
effective_clarifications_for_rules = [...snapshot_sent, ...pending_items];
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1273
1370
|
// ── Step 1: Backoffice validation ──
|
|
1371
|
+
let effective_file_validation = { ...file_validation_results };
|
|
1274
1372
|
const has_backoffice_validation = !!(validation_api_endpoint && validation_rules?.length);
|
|
1275
1373
|
if (has_backoffice_validation) {
|
|
1276
1374
|
update_progress({ status: 'validating', total_steps: 1, completed_steps: 0, current_step: 'Running back-office validation...', error: undefined });
|
|
1277
|
-
const
|
|
1278
|
-
|
|
1279
|
-
const
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
continue;
|
|
1286
|
-
if (current_sent.some(c => c.id === clar_id && (c.status === 'responded' || c.status === 'resolved')))
|
|
1287
|
-
continue;
|
|
1288
|
-
const original = current_drafts.find(c => c.id === clar_id) ?? current_sent.find(c => c.id === clar_id);
|
|
1289
|
-
if (original) {
|
|
1290
|
-
pending_items.push({ ...original, status: 'responded', response_choice: response.response_choice, user_comment: response.user_comment });
|
|
1375
|
+
const resolved_rules = build_resolved_rules_map(effective_clarifications_for_rules);
|
|
1376
|
+
// Also mark accepted rules as resolved so they skip re-validation
|
|
1377
|
+
for (const [, result] of Object.entries(effective_file_validation)) {
|
|
1378
|
+
if (result.rule_results) {
|
|
1379
|
+
for (const rr of result.rule_results) {
|
|
1380
|
+
if (rr.accepted && rr.rule_id) {
|
|
1381
|
+
resolved_rules.set(rr.rule_id, { response_choice: rr.user_resolution?.response_choice ?? 'accepted', user_comment: rr.user_resolution?.user_comment });
|
|
1382
|
+
}
|
|
1291
1383
|
}
|
|
1292
1384
|
}
|
|
1293
|
-
if (pending_items.length > 0) {
|
|
1294
|
-
effective_clarifications = [...current_sent, ...pending_items];
|
|
1295
|
-
}
|
|
1296
1385
|
}
|
|
1297
|
-
const resolved_rules = build_resolved_rules_map(effective_clarifications);
|
|
1298
1386
|
const vr = await validate_classified_files({
|
|
1299
1387
|
classifications: classification_results,
|
|
1300
1388
|
validation_api_endpoint: validation_api_endpoint,
|
|
@@ -1309,37 +1397,140 @@ export function use_llm_run({ props, update_progress, classification_results, se
|
|
|
1309
1397
|
resolved_rules,
|
|
1310
1398
|
});
|
|
1311
1399
|
errors.push(...vr.errors);
|
|
1400
|
+
// Merge backoffice validation results into local copy for eligibility check
|
|
1401
|
+
for (const result of vr.all_validation_results) {
|
|
1402
|
+
effective_file_validation[result.file_id] = result;
|
|
1403
|
+
}
|
|
1312
1404
|
}
|
|
1313
|
-
// ── Step
|
|
1314
|
-
|
|
1315
|
-
const
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1405
|
+
// ── Step 1b: Text classification ──
|
|
1406
|
+
// Extract text from front-office fields (non-file content) and classify it
|
|
1407
|
+
const { field_textbox_configs, llm_api_endpoint } = props;
|
|
1408
|
+
if (field_textbox_configs && llm_api_endpoint) {
|
|
1409
|
+
const text_entries = [];
|
|
1410
|
+
for (const field_id of Object.keys(field_textbox_configs)) {
|
|
1411
|
+
const text = get_text_from_value(front_form_data[field_id]);
|
|
1412
|
+
if (text.trim().length > 0) {
|
|
1413
|
+
text_entries.push({ field_id, text });
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
if (text_entries.length > 0) {
|
|
1417
|
+
update_progress({ current_step: `Classifying ${text_entries.length} text entry(ies)...` });
|
|
1418
|
+
for (const entry of text_entries) {
|
|
1419
|
+
try {
|
|
1420
|
+
const classify_text_start = Date.now();
|
|
1421
|
+
const response = await fetch(llm_api_endpoint, {
|
|
1422
|
+
method: 'POST',
|
|
1423
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1424
|
+
body: JSON.stringify({
|
|
1425
|
+
action: 'classify_text',
|
|
1426
|
+
field_id: entry.field_id,
|
|
1427
|
+
text: entry.text,
|
|
1428
|
+
available_tags: field_textbox_configs[entry.field_id]?.classification?.available_tags ?? props.available_tags ?? [],
|
|
1429
|
+
...(props.available_document_types?.length ? { available_document_types: props.available_document_types } : {}),
|
|
1430
|
+
}),
|
|
1431
|
+
});
|
|
1432
|
+
const result = await response.json();
|
|
1433
|
+
run_log.push({
|
|
1434
|
+
step: 'classify_text',
|
|
1435
|
+
label: `Text Classification: ${entry.field_id}`,
|
|
1436
|
+
request: { action: 'classify_text', field_id: entry.field_id, text_length: entry.text.length },
|
|
1437
|
+
response: result,
|
|
1438
|
+
timestamp: classify_text_start,
|
|
1439
|
+
duration_ms: Date.now() - classify_text_start,
|
|
1440
|
+
});
|
|
1441
|
+
if (result.success && result.tags?.length > 0) {
|
|
1442
|
+
// Add text classification to classification_results so review queue can use categories
|
|
1443
|
+
set_classification_results((prev) => {
|
|
1444
|
+
const text_cls_id = `__text_${entry.field_id}`;
|
|
1445
|
+
const text_file_cls = {
|
|
1446
|
+
file_id: text_cls_id,
|
|
1447
|
+
file_name: `Text from ${entry.field_id}`,
|
|
1448
|
+
tags: result.tags,
|
|
1449
|
+
document_nature: result.document_nature,
|
|
1450
|
+
};
|
|
1451
|
+
const existing = prev.find((c) => c.field_id === entry.field_id);
|
|
1452
|
+
if (existing) {
|
|
1453
|
+
// Merge with existing file classifications
|
|
1454
|
+
const filtered = existing.file_classifications.filter((fc) => fc.file_id !== text_cls_id);
|
|
1455
|
+
return [
|
|
1456
|
+
...prev.filter((c) => c.field_id !== entry.field_id),
|
|
1457
|
+
{
|
|
1458
|
+
...existing,
|
|
1459
|
+
tags: [...new Set([...existing.tags, ...result.tags])],
|
|
1460
|
+
file_classifications: [...filtered, text_file_cls],
|
|
1461
|
+
},
|
|
1462
|
+
];
|
|
1463
|
+
}
|
|
1464
|
+
return [...prev, { field_id: entry.field_id, tags: result.tags, file_classifications: [text_file_cls] }];
|
|
1465
|
+
});
|
|
1466
|
+
}
|
|
1467
|
+
// Create draft clarifications from text issues
|
|
1468
|
+
if (result.success && result.issues?.length > 0) {
|
|
1469
|
+
const text_clarifications = result.issues.map((issue, idx) => ({
|
|
1470
|
+
id: `text_issue_${entry.field_id}_${idx}_${Date.now()}`,
|
|
1471
|
+
type: 'validation_issue',
|
|
1472
|
+
status: 'pending',
|
|
1473
|
+
target_field_id: entry.field_id,
|
|
1474
|
+
target_label: `Text from ${entry.field_id}`,
|
|
1475
|
+
issue_description: issue.description,
|
|
1476
|
+
severity: issue.severity === 'error' ? 'error' : 'warning',
|
|
1477
|
+
doc_references: [{
|
|
1478
|
+
file_id: `__text_${entry.field_id}`,
|
|
1479
|
+
file_name: `Text from ${entry.field_id}`,
|
|
1480
|
+
mime_type: 'text/plain',
|
|
1481
|
+
}],
|
|
1482
|
+
response_options: [
|
|
1483
|
+
{ value: 'acknowledge', label: 'Acknowledge' },
|
|
1484
|
+
{ value: 'will_fix', label: 'Will fix' },
|
|
1485
|
+
{ value: 'ignore', label: 'Ignore' },
|
|
1486
|
+
],
|
|
1487
|
+
created_at: new Date().toISOString(),
|
|
1488
|
+
}));
|
|
1489
|
+
set_file_validation_results((prev) => ({
|
|
1490
|
+
...prev,
|
|
1491
|
+
[`__text_${entry.field_id}`]: {
|
|
1492
|
+
file_id: `__text_${entry.field_id}`,
|
|
1493
|
+
file_name: `Text from ${entry.field_id}`,
|
|
1494
|
+
status: 'failed',
|
|
1495
|
+
errors: text_clarifications,
|
|
1496
|
+
document_types: [],
|
|
1497
|
+
},
|
|
1498
|
+
}));
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
catch (err) {
|
|
1502
|
+
errors.push({ step: `classify_text:${entry.field_id}`, error: err instanceof Error ? err.message : 'Text classification failed' });
|
|
1503
|
+
logger.error('[use_llm_run] classify_text_error', { field_id: entry.field_id, error: err instanceof Error ? err.message : String(err) });
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
logger.info('[use_llm_run] text_classification_phase_complete', { text_entry_count: text_entries.length });
|
|
1334
1507
|
}
|
|
1335
1508
|
}
|
|
1336
|
-
//
|
|
1509
|
+
// Clear queued state — autofill will re-queue eligible files via on_queue_files
|
|
1510
|
+
if (all_classified_file_ids.length > 0) {
|
|
1511
|
+
remove_queued_files(all_classified_file_ids);
|
|
1512
|
+
}
|
|
1513
|
+
// ── Step 2: Determine eligible files and route + autofill ──
|
|
1514
|
+
update_progress({ status: 'routing', total_steps: 1, completed_steps: 0, current_step: 'Routing files to back-office...', error: undefined });
|
|
1515
|
+
// Determine eligible files — use effective_file_validation which merges backoffice results
|
|
1337
1516
|
const eligible_classifications = [];
|
|
1338
1517
|
for (const cls of classification_results) {
|
|
1339
1518
|
const eligible_file_cls = [];
|
|
1340
1519
|
for (const fc of cls.file_classifications) {
|
|
1341
|
-
const vr =
|
|
1342
|
-
|
|
1520
|
+
const vr = effective_file_validation[fc.file_id];
|
|
1521
|
+
// Exclude files with any rule sent back to client (pending client fix)
|
|
1522
|
+
const has_sent_back = vr?.rule_results?.some(rr => rr.sent_back);
|
|
1523
|
+
if (has_sent_back) {
|
|
1524
|
+
logger.warn('[use_llm_run] backoffice: file ineligible — has sent-back rule pending client response', { file_id: fc.file_id });
|
|
1525
|
+
continue;
|
|
1526
|
+
}
|
|
1527
|
+
// Exclude files with unreviewed resolved rules (pending agent acceptance)
|
|
1528
|
+
const resolved_rules_for_file = vr?.rule_results?.filter(rr => rr.has_issue && rr.user_resolved) ?? [];
|
|
1529
|
+
if (resolved_rules_for_file.length > 0 && !resolved_rules_for_file.every(rr => rr.accepted)) {
|
|
1530
|
+
logger.warn('[use_llm_run] backoffice: file ineligible — has unaccepted resolved rules', { file_id: fc.file_id });
|
|
1531
|
+
continue;
|
|
1532
|
+
}
|
|
1533
|
+
if (!vr || vr.status === 'passed' || vr.status === 'skipped' || vr.status === 'pending' || vr.status === 'validating' || vr.status === 'manual_review') {
|
|
1343
1534
|
eligible_file_cls.push(fc);
|
|
1344
1535
|
}
|
|
1345
1536
|
else if (vr.status === 'failed') {
|
|
@@ -1351,6 +1542,11 @@ export function use_llm_run({ props, update_progress, classification_results, se
|
|
|
1351
1542
|
});
|
|
1352
1543
|
if (all_resolved)
|
|
1353
1544
|
eligible_file_cls.push(fc);
|
|
1545
|
+
else
|
|
1546
|
+
logger.warn('[use_llm_run] backoffice: file ineligible — failed with unresolved errors', { file_id: fc.file_id, error_count: vr.errors.length, errors: vr.errors.map(e => ({ id: e.id, status: e.status, rule_id: e.rule_id })) });
|
|
1547
|
+
}
|
|
1548
|
+
else {
|
|
1549
|
+
logger.warn('[use_llm_run] backoffice: file ineligible — unexpected status', { file_id: fc.file_id, status: vr.status });
|
|
1354
1550
|
}
|
|
1355
1551
|
}
|
|
1356
1552
|
if (eligible_file_cls.length > 0) {
|
|
@@ -1415,6 +1611,12 @@ export function use_llm_run({ props, update_progress, classification_results, se
|
|
|
1415
1611
|
set_group_autofill_log,
|
|
1416
1612
|
run_log,
|
|
1417
1613
|
on_autofill_file: handle_autofill_file,
|
|
1614
|
+
on_queue_files: (file_ids, queued) => {
|
|
1615
|
+
if (queued)
|
|
1616
|
+
add_queued_files(file_ids);
|
|
1617
|
+
else
|
|
1618
|
+
remove_queued_files(file_ids);
|
|
1619
|
+
},
|
|
1418
1620
|
force_autofill: true,
|
|
1419
1621
|
});
|
|
1420
1622
|
set_unassigned_files(routing_result.unassigned);
|