@nyaruka/temba-components 0.131.1 → 0.131.3
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/.github/workflows/publish.yml +4 -1
- package/CHANGELOG.md +75 -1
- package/demo/components/floating-tabs/example.html +400 -0
- package/demo/components/flow/index.html +1 -1
- package/demo/data/flows/food-order.json +2 -2
- package/demo/data/flows/sample-flow.json +113 -125
- package/demo/data/flows/voicemail.json +613 -0
- package/demo/index.html +6 -0
- package/dist/locales/es.js +5 -5
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/fr.js +5 -5
- package/dist/locales/fr.js.map +1 -1
- package/dist/locales/locale-codes.js +11 -2
- package/dist/locales/locale-codes.js.map +1 -1
- package/dist/locales/pt.js +5 -5
- package/dist/locales/pt.js.map +1 -1
- package/dist/static/svg/index.svg +1 -1
- package/dist/temba-components.js +1773 -662
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/Icons.js +4 -1
- package/out-tsc/src/Icons.js.map +1 -1
- package/out-tsc/src/display/FloatingTab.js +167 -0
- package/out-tsc/src/display/FloatingTab.js.map +1 -0
- package/out-tsc/src/display/ProgressBar.js +22 -2
- package/out-tsc/src/display/ProgressBar.js.map +1 -1
- package/out-tsc/src/events.js.map +1 -1
- package/out-tsc/src/flow/CanvasMenu.js +200 -0
- package/out-tsc/src/flow/CanvasMenu.js.map +1 -0
- package/out-tsc/src/flow/CanvasNode.js +489 -47
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +1417 -67
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +479 -112
- package/out-tsc/src/flow/NodeEditor.js.map +1 -1
- package/out-tsc/src/flow/NodeTypeSelector.js +540 -0
- package/out-tsc/src/flow/NodeTypeSelector.js.map +1 -0
- package/out-tsc/src/flow/StickyNote.js +12 -3
- package/out-tsc/src/flow/StickyNote.js.map +1 -1
- package/out-tsc/src/flow/actions/add_contact_groups.js +4 -3
- package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
- package/out-tsc/src/flow/actions/add_contact_urn.js +63 -4
- package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
- package/out-tsc/src/flow/actions/add_input_labels.js +4 -3
- package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
- package/out-tsc/src/flow/actions/play_audio.js +3 -2
- package/out-tsc/src/flow/actions/play_audio.js.map +1 -1
- package/out-tsc/src/flow/actions/remove_contact_groups.js +7 -5
- package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -1
- package/out-tsc/src/flow/actions/request_optin.js +3 -2
- package/out-tsc/src/flow/actions/request_optin.js.map +1 -1
- package/out-tsc/src/flow/actions/say_msg.js +3 -2
- package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
- package/out-tsc/src/flow/actions/send_broadcast.js +77 -23
- package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
- package/out-tsc/src/flow/actions/send_email.js +5 -5
- package/out-tsc/src/flow/actions/send_email.js.map +1 -1
- package/out-tsc/src/flow/actions/send_msg.js +101 -21
- package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_channel.js +6 -9
- package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_field.js +20 -20
- package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_language.js +3 -2
- package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_name.js +3 -12
- package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_status.js +3 -2
- package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
- package/out-tsc/src/flow/actions/set_run_result.js +4 -3
- package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
- package/out-tsc/src/flow/actions/start_session.js +181 -6
- package/out-tsc/src/flow/actions/start_session.js.map +1 -1
- package/out-tsc/src/flow/config.js +11 -23
- package/out-tsc/src/flow/config.js.map +1 -1
- package/out-tsc/src/flow/currencies.js +45 -0
- package/out-tsc/src/flow/currencies.js.map +1 -0
- package/out-tsc/src/flow/nodes/shared-rules.js +257 -0
- package/out-tsc/src/flow/nodes/shared-rules.js.map +1 -0
- package/out-tsc/src/flow/nodes/shared.js +71 -0
- package/out-tsc/src/flow/nodes/shared.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_airtime.js +211 -5
- package/out-tsc/src/flow/nodes/split_by_airtime.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_contact_field.js +152 -3
- package/out-tsc/src/flow/nodes/split_by_contact_field.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_expression.js +73 -2
- package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_groups.js +18 -10
- package/out-tsc/src/flow/nodes/split_by_groups.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_intent.js +8 -0
- package/out-tsc/src/flow/nodes/split_by_intent.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_llm.js +11 -3
- package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +10 -3
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_random.js +10 -4
- package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_resthook.js +113 -0
- package/out-tsc/src/flow/nodes/split_by_resthook.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_run_result.js +211 -3
- package/out-tsc/src/flow/nodes/split_by_run_result.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_scheme.js +158 -2
- package/out-tsc/src/flow/nodes/split_by_scheme.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_subflow.js +13 -5
- package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_ticket.js +10 -3
- package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_webhook.js +10 -3
- package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_digits.js +3 -2
- package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_menu.js +3 -2
- package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js +38 -568
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
- package/out-tsc/src/flow/types.js +86 -12
- package/out-tsc/src/flow/types.js.map +1 -1
- package/out-tsc/src/flow/utils.js +101 -14
- package/out-tsc/src/flow/utils.js.map +1 -1
- package/out-tsc/src/form/FieldRenderer.js +2 -4
- package/out-tsc/src/form/FieldRenderer.js.map +1 -1
- package/out-tsc/src/interfaces.js +3 -0
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/src/layout/FloatingWindow.js +346 -0
- package/out-tsc/src/layout/FloatingWindow.js.map +1 -0
- package/out-tsc/src/list/SortableList.js +98 -33
- package/out-tsc/src/list/SortableList.js.map +1 -1
- package/out-tsc/src/live/ContactChat.js +6 -25
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/src/locales/es.js +5 -5
- package/out-tsc/src/locales/es.js.map +1 -1
- package/out-tsc/src/locales/fr.js +5 -5
- package/out-tsc/src/locales/fr.js.map +1 -1
- package/out-tsc/src/locales/locale-codes.js +11 -2
- package/out-tsc/src/locales/locale-codes.js.map +1 -1
- package/out-tsc/src/locales/pt.js +5 -5
- package/out-tsc/src/locales/pt.js.map +1 -1
- package/out-tsc/src/store/AppState.js +120 -0
- package/out-tsc/src/store/AppState.js.map +1 -1
- package/out-tsc/src/utils.js +254 -13
- package/out-tsc/src/utils.js.map +1 -1
- package/out-tsc/temba-modules.js +8 -0
- package/out-tsc/temba-modules.js.map +1 -1
- package/out-tsc/test/ActionHelper.js +3 -3
- package/out-tsc/test/ActionHelper.js.map +1 -1
- package/out-tsc/test/NodeHelper.js +6 -3
- package/out-tsc/test/NodeHelper.js.map +1 -1
- package/out-tsc/test/actions/add_contact_urn.test.js +202 -0
- package/out-tsc/test/actions/add_contact_urn.test.js.map +1 -0
- package/out-tsc/test/actions/send_broadcast.test.js +148 -0
- package/out-tsc/test/actions/send_broadcast.test.js.map +1 -0
- package/out-tsc/test/actions/send_email.test.js +17 -23
- package/out-tsc/test/actions/send_email.test.js.map +1 -1
- package/out-tsc/test/actions/send_msg.test.js +33 -15
- package/out-tsc/test/actions/send_msg.test.js.map +1 -1
- package/out-tsc/test/actions/start_session.test.js +116 -0
- package/out-tsc/test/actions/start_session.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_airtime.test.js +604 -0
- package/out-tsc/test/nodes/split_by_airtime.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_contact_field.test.js +387 -0
- package/out-tsc/test/nodes/split_by_contact_field.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_expression.test.js +614 -0
- package/out-tsc/test/nodes/split_by_expression.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_random.test.js +3 -3
- package/out-tsc/test/nodes/split_by_random.test.js.map +1 -1
- package/out-tsc/test/nodes/split_by_resthook.test.js +337 -0
- package/out-tsc/test/nodes/split_by_resthook.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_run_result.test.js +920 -0
- package/out-tsc/test/nodes/split_by_run_result.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_scheme.test.js +399 -0
- package/out-tsc/test/nodes/split_by_scheme.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_subflow.test.js +333 -0
- package/out-tsc/test/nodes/split_by_subflow.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_digits.test.js +2 -2
- package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -1
- package/out-tsc/test/nodes/wait_for_response.test.js +2 -1
- package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -1
- package/out-tsc/test/temba-action-drag-between-nodes.test.js +252 -0
- package/out-tsc/test/temba-action-drag-between-nodes.test.js.map +1 -0
- package/out-tsc/test/temba-canvas-menu.test.js +122 -0
- package/out-tsc/test/temba-canvas-menu.test.js.map +1 -0
- package/out-tsc/test/temba-floating-tab.test.js +91 -0
- package/out-tsc/test/temba-floating-tab.test.js.map +1 -0
- package/out-tsc/test/temba-floating-window.test.js +301 -0
- package/out-tsc/test/temba-floating-window.test.js.map +1 -0
- package/out-tsc/test/temba-flow-editor-node.test.js +202 -2
- package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
- package/out-tsc/test/temba-flow-editor.test.js +7 -8
- package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
- package/out-tsc/test/temba-localization.test.js +471 -0
- package/out-tsc/test/temba-localization.test.js.map +1 -0
- package/out-tsc/test/temba-node-editor.test.js +3 -1
- package/out-tsc/test/temba-node-editor.test.js.map +1 -1
- package/out-tsc/test/temba-node-type-selector.test.js +265 -0
- package/out-tsc/test/temba-node-type-selector.test.js.map +1 -0
- package/out-tsc/test/temba-omnibox.test.js +2 -1
- package/out-tsc/test/temba-omnibox.test.js.map +1 -1
- package/out-tsc/test/temba-sortable-list.test.js +51 -0
- package/out-tsc/test/temba-sortable-list.test.js.map +1 -1
- package/out-tsc/test/temba-utils-index.test.js +1 -27
- package/out-tsc/test/temba-utils-index.test.js.map +1 -1
- package/out-tsc/test/utils.test.js +20 -0
- package/out-tsc/test/utils.test.js.map +1 -1
- package/package.json +2 -1
- package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/multiple-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/single-group.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/expression-facebook.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/expression-phone.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/facebook-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/instagram-handle.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/line-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/phone-number.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/telegram-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/viber-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/wechat-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/whatsapp.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/expression-facebook.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/expression-phone.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/facebook-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/instagram-handle.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/line-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/phone-number.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/telegram-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/viber-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/wechat-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/whatsapp.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/contacts-only.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/groups-only.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/many-groups.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/multiline-text.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/with-attachments.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/contacts-only.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/groups-only.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/many-groups.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/multiline-text.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/with-attachments.png +0 -0
- package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
- package/screenshots/truth/actions/send_email/editor/empty-body.png +0 -0
- package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
- package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
- package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
- package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
- package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
- package/screenshots/truth/actions/send_email/editor/with-expressions.png +0 -0
- package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
- package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
- package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
- package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
- package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
- package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
- package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
- package/screenshots/truth/actions/send_email/render/with-expressions.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-without-quick-replies.png +0 -0
- package/screenshots/truth/actions/start_session/editor/contact-query.png +0 -0
- package/screenshots/truth/actions/start_session/editor/contacts-only.png +0 -0
- package/screenshots/truth/actions/start_session/editor/create-contact.png +0 -0
- package/screenshots/truth/actions/start_session/editor/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/start_session/editor/groups-only.png +0 -0
- package/screenshots/truth/actions/start_session/editor/many-recipients.png +0 -0
- package/screenshots/truth/actions/start_session/render/contact-query.png +0 -0
- package/screenshots/truth/actions/start_session/render/contacts-only.png +0 -0
- package/screenshots/truth/actions/start_session/render/create-contact.png +0 -0
- package/screenshots/truth/actions/start_session/render/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/start_session/render/groups-only.png +0 -0
- package/screenshots/truth/actions/start_session/render/many-recipients.png +0 -0
- package/screenshots/truth/canvas-menu/open.png +0 -0
- package/screenshots/truth/editor/router.png +0 -0
- package/screenshots/truth/editor/wait.png +0 -0
- package/screenshots/truth/floating-tab/default.png +0 -0
- package/screenshots/truth/floating-tab/gray.png +0 -0
- package/screenshots/truth/floating-tab/green.png +0 -0
- package/screenshots/truth/floating-tab/hidden.png +0 -0
- package/screenshots/truth/floating-tab/hover.png +0 -0
- package/screenshots/truth/floating-tab/purple.png +0 -0
- package/screenshots/truth/floating-window/chromeless.png +0 -0
- package/screenshots/truth/floating-window/custom-size.png +0 -0
- package/screenshots/truth/floating-window/default.png +0 -0
- package/screenshots/truth/floating-window/with-header.png +0 -0
- package/screenshots/truth/list/fields-dragging.png +0 -0
- package/screenshots/truth/list/sortable-dragging.png +0 -0
- package/screenshots/truth/node-type-selector/action-mode.png +0 -0
- package/screenshots/truth/node-type-selector/split-mode.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/editor/information-extraction.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/editor/sentiment-analysis.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/editor/summarization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/editor/translation-task.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/summarization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/translation-task.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/basic-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/custom-input-and-result-name.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/many-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/minimal-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/ab-test-multiple-variants.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/sampling-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/three-way-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/two-bucket-split.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
- package/src/Icons.ts +4 -1
- package/src/display/FloatingTab.ts +174 -0
- package/src/display/ProgressBar.ts +22 -2
- package/src/events.ts +2 -8
- package/src/flow/CanvasMenu.ts +217 -0
- package/src/flow/CanvasNode.ts +596 -40
- package/src/flow/Editor.ts +1721 -45
- package/src/flow/NodeEditor.ts +621 -144
- package/src/flow/NodeTypeSelector.ts +636 -0
- package/src/flow/StickyNote.ts +12 -3
- package/src/flow/actions/add_contact_groups.ts +5 -4
- package/src/flow/actions/add_contact_urn.ts +78 -4
- package/src/flow/actions/add_input_labels.ts +5 -4
- package/src/flow/actions/play_audio.ts +3 -2
- package/src/flow/actions/remove_contact_groups.ts +16 -6
- package/src/flow/actions/request_optin.ts +3 -2
- package/src/flow/actions/say_msg.ts +3 -2
- package/src/flow/actions/send_broadcast.ts +86 -23
- package/src/flow/actions/send_email.ts +12 -6
- package/src/flow/actions/send_msg.ts +155 -34
- package/src/flow/actions/set_contact_channel.ts +6 -11
- package/src/flow/actions/set_contact_field.ts +21 -25
- package/src/flow/actions/set_contact_language.ts +11 -4
- package/src/flow/actions/set_contact_name.ts +4 -15
- package/src/flow/actions/set_contact_status.ts +4 -3
- package/src/flow/actions/set_run_result.ts +5 -4
- package/src/flow/actions/start_session.ts +210 -6
- package/src/flow/config.ts +11 -23
- package/src/flow/currencies.ts +51 -0
- package/src/flow/nodes/shared-rules.ts +301 -0
- package/src/flow/nodes/shared.ts +87 -0
- package/src/flow/nodes/split_by_airtime.ts +255 -5
- package/src/flow/nodes/split_by_contact_field.ts +195 -3
- package/src/flow/nodes/split_by_expression.ts +104 -2
- package/src/flow/nodes/split_by_groups.ts +26 -11
- package/src/flow/nodes/split_by_intent.ts +8 -0
- package/src/flow/nodes/split_by_llm.ts +22 -4
- package/src/flow/nodes/split_by_llm_categorize.ts +22 -5
- package/src/flow/nodes/split_by_random.ts +16 -6
- package/src/flow/nodes/split_by_resthook.ts +140 -0
- package/src/flow/nodes/split_by_run_result.ts +259 -3
- package/src/flow/nodes/split_by_scheme.ts +202 -2
- package/src/flow/nodes/split_by_subflow.ts +17 -5
- package/src/flow/nodes/split_by_ticket.ts +15 -4
- package/src/flow/nodes/split_by_webhook.ts +17 -6
- package/src/flow/nodes/wait_for_digits.ts +3 -2
- package/src/flow/nodes/wait_for_menu.ts +3 -2
- package/src/flow/nodes/wait_for_response.ts +59 -680
- package/src/flow/types.ts +156 -23
- package/src/flow/utils.ts +108 -14
- package/src/form/FieldRenderer.ts +2 -4
- package/src/interfaces.ts +3 -0
- package/src/layout/FloatingWindow.ts +386 -0
- package/src/list/SortableList.ts +109 -34
- package/src/live/ContactChat.ts +7 -25
- package/src/locales/es.ts +18 -13
- package/src/locales/fr.ts +18 -13
- package/src/locales/locale-codes.ts +11 -2
- package/src/locales/pt.ts +18 -13
- package/src/store/AppState.ts +173 -0
- package/src/store/flow-definition.d.ts +2 -5
- package/src/utils.ts +332 -12
- package/static/api/channels.json +46 -0
- package/static/api/llms.json +18 -0
- package/static/api/resthooks.json +31 -0
- package/static/svg/index.svg +1 -1
- package/static/svg/work/traced/lightning-02.svg +1 -0
- package/static/svg/work/used/lightning-02.svg +3 -0
- package/temba-modules.ts +8 -0
- package/test/ActionHelper.ts +3 -3
- package/test/NodeHelper.ts +6 -3
- package/test/actions/add_contact_urn.test.ts +287 -0
- package/test/actions/send_broadcast.test.ts +190 -0
- package/test/actions/send_email.test.ts +17 -23
- package/test/actions/send_msg.test.ts +39 -15
- package/test/actions/start_session.test.ts +151 -0
- package/test/nodes/split_by_airtime.test.ts +673 -0
- package/test/nodes/split_by_contact_field.test.ts +451 -0
- package/test/nodes/split_by_expression.test.ts +751 -0
- package/test/nodes/split_by_random.test.ts +3 -3
- package/test/nodes/split_by_resthook.test.ts +398 -0
- package/test/nodes/split_by_run_result.test.ts +1109 -0
- package/test/nodes/split_by_scheme.test.ts +486 -0
- package/test/nodes/split_by_subflow.test.ts +381 -0
- package/test/nodes/wait_for_digits.test.ts +2 -2
- package/test/nodes/wait_for_response.test.ts +2 -1
- package/test/temba-action-drag-between-nodes.test.ts +301 -0
- package/test/temba-canvas-menu.test.ts +156 -0
- package/test/temba-floating-tab.test.ts +110 -0
- package/test/temba-floating-window.test.ts +477 -0
- package/test/temba-flow-editor-node.test.ts +246 -2
- package/test/temba-flow-editor.test.ts +7 -8
- package/test/temba-localization.test.ts +611 -0
- package/test/temba-node-editor.test.ts +3 -1
- package/test/temba-node-type-selector.test.ts +355 -0
- package/test/temba-omnibox.test.ts +2 -1
- package/test/temba-sortable-list.test.ts +69 -0
- package/test/temba-utils-index.test.ts +0 -35
- package/test/utils.test.ts +22 -0
- package/test-assets/contacts/history.json +14 -21
- package/test-assets/select/llms.json +2 -2
- package/web-dev-server.config.mjs +49 -1
- package/web-test-runner.config.mjs +0 -1
- package/out-tsc/src/flow/actions/call_classifier.js +0 -11
- package/out-tsc/src/flow/actions/call_classifier.js.map +0 -1
- package/out-tsc/src/flow/actions/call_resthook.js +0 -11
- package/out-tsc/src/flow/actions/call_resthook.js.map +0 -1
- package/out-tsc/src/flow/actions/split_by_expression_example.js +0 -77
- package/out-tsc/src/flow/actions/split_by_expression_example.js.map +0 -1
- package/out-tsc/src/flow/actions/transfer_airtime.js +0 -11
- package/out-tsc/src/flow/actions/transfer_airtime.js.map +0 -1
- package/out-tsc/src/flow/nodes/wait_for_audio.js +0 -7
- package/out-tsc/src/flow/nodes/wait_for_audio.js.map +0 -1
- package/out-tsc/src/flow/nodes/wait_for_image.js +0 -7
- package/out-tsc/src/flow/nodes/wait_for_image.js.map +0 -1
- package/out-tsc/src/flow/nodes/wait_for_location.js +0 -7
- package/out-tsc/src/flow/nodes/wait_for_location.js.map +0 -1
- package/out-tsc/src/flow/nodes/wait_for_video.js +0 -7
- package/out-tsc/src/flow/nodes/wait_for_video.js.map +0 -1
- package/src/flow/actions/call_classifier.ts +0 -12
- package/src/flow/actions/call_resthook.ts +0 -12
- package/src/flow/actions/split_by_expression_example.ts +0 -88
- package/src/flow/actions/transfer_airtime.ts +0 -12
- package/src/flow/nodes/wait_for_audio.ts +0 -7
- package/src/flow/nodes/wait_for_image.ts +0 -7
- package/src/flow/nodes/wait_for_location.ts +0 -7
- package/src/flow/nodes/wait_for_video.ts +0 -7
|
@@ -1,11 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SPLIT_GROUPS, FormData, NodeConfig, FlowTypes } from '../types';
|
|
2
2
|
import { Node, Category, Exit, Case } from '../../store/flow-definition';
|
|
3
|
-
import { generateUUID } from '../../utils';
|
|
3
|
+
import { generateUUID, createRulesRouter } from '../../utils';
|
|
4
4
|
import {
|
|
5
5
|
getWaitForResponseOperators,
|
|
6
6
|
operatorsToSelectOptions,
|
|
7
7
|
getOperatorConfig
|
|
8
8
|
} from '../operators';
|
|
9
|
+
import {
|
|
10
|
+
resultNameField,
|
|
11
|
+
categoriesToLocalizationFormData,
|
|
12
|
+
localizationFormDataToCategories
|
|
13
|
+
} from './shared';
|
|
14
|
+
import {
|
|
15
|
+
createRulesArrayConfig,
|
|
16
|
+
extractUserRules,
|
|
17
|
+
casesToFormRules
|
|
18
|
+
} from './shared-rules';
|
|
9
19
|
|
|
10
20
|
const TIMEOUT_OPTIONS = [
|
|
11
21
|
{ value: '60', name: '1 minute' },
|
|
@@ -28,318 +38,23 @@ const TIMEOUT_OPTIONS = [
|
|
|
28
38
|
{ value: '604800', name: '1 week' }
|
|
29
39
|
];
|
|
30
40
|
|
|
31
|
-
// Helper function to check if a category is a system category
|
|
32
|
-
const isSystemCategory = (categoryName: string): boolean => {
|
|
33
|
-
return ['No Response', 'Other', 'All Responses', 'Timeout'].includes(
|
|
34
|
-
categoryName
|
|
35
|
-
);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
// Helper function to check if a UUID belongs to a system category
|
|
39
|
-
const isSystemCategoryUuid = (
|
|
40
|
-
uuid: string,
|
|
41
|
-
categories: Category[]
|
|
42
|
-
): boolean => {
|
|
43
|
-
const category = categories.find((cat) => cat.uuid === uuid);
|
|
44
|
-
return category ? isSystemCategory(category.name) : false;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
// Helper function to generate default category name based on operator and operands
|
|
48
|
-
const generateDefaultCategoryName = (
|
|
49
|
-
operator: string,
|
|
50
|
-
value1?: string,
|
|
51
|
-
value2?: string
|
|
52
|
-
): string => {
|
|
53
|
-
const operatorConfig = getOperatorConfig(operator);
|
|
54
|
-
if (!operatorConfig) return '';
|
|
55
|
-
|
|
56
|
-
// Fixed category names (no operands)
|
|
57
|
-
if (operatorConfig.operands === 0) {
|
|
58
|
-
return operatorConfig.categoryName || '';
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Dynamic category names based on operands
|
|
62
|
-
const cleanValue1 = (value1 || '').trim();
|
|
63
|
-
const cleanValue2 = (value2 || '').trim();
|
|
64
|
-
|
|
65
|
-
// Helper to capitalize first letter
|
|
66
|
-
const capitalize = (str: string) => {
|
|
67
|
-
if (!str) return '';
|
|
68
|
-
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
// Handle different operator types
|
|
72
|
-
switch (operator) {
|
|
73
|
-
// Word/phrase operators - capitalize first letter of value
|
|
74
|
-
case 'has_any_word':
|
|
75
|
-
case 'has_all_words':
|
|
76
|
-
case 'has_phrase':
|
|
77
|
-
case 'has_only_phrase':
|
|
78
|
-
case 'has_beginning':
|
|
79
|
-
return cleanValue1 ? capitalize(cleanValue1) : '';
|
|
80
|
-
|
|
81
|
-
// Pattern operators - show as-is
|
|
82
|
-
case 'has_pattern':
|
|
83
|
-
return cleanValue1;
|
|
84
|
-
|
|
85
|
-
// Number comparison operators - include symbol
|
|
86
|
-
case 'has_number_eq':
|
|
87
|
-
return cleanValue1 ? `= ${cleanValue1}` : '';
|
|
88
|
-
case 'has_number_lt':
|
|
89
|
-
return cleanValue1 ? `< ${cleanValue1}` : '';
|
|
90
|
-
case 'has_number_lte':
|
|
91
|
-
return cleanValue1 ? `≤ ${cleanValue1}` : '';
|
|
92
|
-
case 'has_number_gt':
|
|
93
|
-
return cleanValue1 ? `> ${cleanValue1}` : '';
|
|
94
|
-
case 'has_number_gte':
|
|
95
|
-
return cleanValue1 ? `≥ ${cleanValue1}` : '';
|
|
96
|
-
|
|
97
|
-
// Number between - range format
|
|
98
|
-
case 'has_number_between':
|
|
99
|
-
if (cleanValue1 && cleanValue2) {
|
|
100
|
-
return `${cleanValue1} - ${cleanValue2}`;
|
|
101
|
-
}
|
|
102
|
-
return '';
|
|
103
|
-
|
|
104
|
-
// Date operators - format with relative expressions
|
|
105
|
-
case 'has_date_lt':
|
|
106
|
-
case 'has_date_lte':
|
|
107
|
-
if (cleanValue1) {
|
|
108
|
-
// Parse relative date expression (e.g., "today + 5" or "today - 3")
|
|
109
|
-
const match = cleanValue1.match(/^(today)\s*([+-])\s*(\d+)$/i);
|
|
110
|
-
if (match) {
|
|
111
|
-
const [, base, operator, days] = match;
|
|
112
|
-
const dayWord = days === '1' ? 'day' : 'days';
|
|
113
|
-
return `Before ${base} ${operator} ${days} ${dayWord}`;
|
|
114
|
-
}
|
|
115
|
-
// Fallback for other date formats
|
|
116
|
-
return `Before ${cleanValue1}`;
|
|
117
|
-
}
|
|
118
|
-
return '';
|
|
119
|
-
|
|
120
|
-
case 'has_date_gt':
|
|
121
|
-
case 'has_date_gte':
|
|
122
|
-
if (cleanValue1) {
|
|
123
|
-
// Parse relative date expression
|
|
124
|
-
const match = cleanValue1.match(/^(today)\s*([+-])\s*(\d+)$/i);
|
|
125
|
-
if (match) {
|
|
126
|
-
const [, base, operator, days] = match;
|
|
127
|
-
const dayWord = days === '1' ? 'day' : 'days';
|
|
128
|
-
return `After ${base} ${operator} ${days} ${dayWord}`;
|
|
129
|
-
}
|
|
130
|
-
// Fallback for other date formats
|
|
131
|
-
return `After ${cleanValue1}`;
|
|
132
|
-
}
|
|
133
|
-
return '';
|
|
134
|
-
|
|
135
|
-
case 'has_date_eq':
|
|
136
|
-
if (cleanValue1) {
|
|
137
|
-
// Parse relative date expression
|
|
138
|
-
const match = cleanValue1.match(/^(today)\s*([+-])\s*(\d+)$/i);
|
|
139
|
-
if (match) {
|
|
140
|
-
const [, base, operator, days] = match;
|
|
141
|
-
const dayWord = days === '1' ? 'day' : 'days';
|
|
142
|
-
return `${base} ${operator} ${days} ${dayWord}`;
|
|
143
|
-
}
|
|
144
|
-
return cleanValue1;
|
|
145
|
-
}
|
|
146
|
-
return '';
|
|
147
|
-
|
|
148
|
-
default:
|
|
149
|
-
// Fallback - capitalize first value
|
|
150
|
-
return cleanValue1 ? capitalize(cleanValue1) : '';
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
|
|
154
41
|
// Helper function to create a wait_for_response router with user rules
|
|
42
|
+
// This is a thin wrapper around createRulesRouter that adds the No Response category for timeouts
|
|
155
43
|
const createWaitForResponseRouter = (
|
|
156
44
|
userRules: any[],
|
|
157
45
|
existingCategories: Category[] = [],
|
|
158
46
|
existingExits: Exit[] = [],
|
|
159
47
|
existingCases: Case[] = []
|
|
160
48
|
) => {
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
49
|
+
const { router, exits } = createRulesRouter(
|
|
50
|
+
'@input.text',
|
|
51
|
+
userRules,
|
|
52
|
+
getOperatorConfig,
|
|
53
|
+
existingCategories,
|
|
54
|
+
existingExits,
|
|
55
|
+
existingCases
|
|
168
56
|
);
|
|
169
57
|
|
|
170
|
-
// Track categories as we create them (case-insensitive lookup)
|
|
171
|
-
const createdCategories = new Map<
|
|
172
|
-
string,
|
|
173
|
-
{ uuid: string; name: string; exit_uuid: string }
|
|
174
|
-
>();
|
|
175
|
-
|
|
176
|
-
// Process rules in their original order to preserve rule order
|
|
177
|
-
userRules.forEach((rule, ruleIndex) => {
|
|
178
|
-
const categoryKey = rule.category.trim().toLowerCase();
|
|
179
|
-
const categoryName = rule.category.trim(); // Use original casing
|
|
180
|
-
|
|
181
|
-
let categoryInfo = createdCategories.get(categoryKey);
|
|
182
|
-
|
|
183
|
-
if (!categoryInfo) {
|
|
184
|
-
// First time seeing this category - create it
|
|
185
|
-
|
|
186
|
-
// Smart category matching: try by name first, then fall back to position
|
|
187
|
-
let existingCategory = existingUserCategories.find(
|
|
188
|
-
(cat) => cat.name.toLowerCase() === categoryKey
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
// If no match by name, try by position (for category rename scenarios)
|
|
192
|
-
const categoryCreationOrder = Array.from(createdCategories.keys()).length;
|
|
193
|
-
if (
|
|
194
|
-
!existingCategory &&
|
|
195
|
-
categoryCreationOrder < existingUserCategories.length
|
|
196
|
-
) {
|
|
197
|
-
const candidateCategory = existingUserCategories[categoryCreationOrder];
|
|
198
|
-
// Double-check that this candidate is not a system category UUID
|
|
199
|
-
if (
|
|
200
|
-
candidateCategory &&
|
|
201
|
-
!isSystemCategoryUuid(candidateCategory.uuid, existingCategories)
|
|
202
|
-
) {
|
|
203
|
-
existingCategory = candidateCategory;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const existingExit = existingCategory
|
|
208
|
-
? existingExits.find((exit) => exit.uuid === existingCategory.exit_uuid)
|
|
209
|
-
: null;
|
|
210
|
-
|
|
211
|
-
// Generate UUIDs, ensuring we don't reuse system category UUIDs
|
|
212
|
-
let exitUuid = existingExit?.uuid || generateUUID();
|
|
213
|
-
let categoryUuid = existingCategory?.uuid || generateUUID();
|
|
214
|
-
|
|
215
|
-
// Additional safety check: if somehow we got a system category UUID, generate new ones
|
|
216
|
-
if (isSystemCategoryUuid(categoryUuid, existingCategories)) {
|
|
217
|
-
categoryUuid = generateUUID();
|
|
218
|
-
exitUuid = generateUUID();
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
categoryInfo = {
|
|
222
|
-
uuid: categoryUuid,
|
|
223
|
-
name: categoryName,
|
|
224
|
-
exit_uuid: exitUuid
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
createdCategories.set(categoryKey, categoryInfo);
|
|
228
|
-
|
|
229
|
-
// Add category and exit
|
|
230
|
-
categories.push({
|
|
231
|
-
uuid: categoryUuid,
|
|
232
|
-
name: categoryName,
|
|
233
|
-
exit_uuid: exitUuid
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
exits.push({
|
|
237
|
-
uuid: exitUuid,
|
|
238
|
-
destination_uuid: existingExit?.destination_uuid || null
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Create case for this rule
|
|
243
|
-
let existingCase = existingCases[ruleIndex];
|
|
244
|
-
|
|
245
|
-
// If we can't find by position, try to find by matching rule content
|
|
246
|
-
if (!existingCase && existingCases.length > 0) {
|
|
247
|
-
existingCase = existingCases.find((case_) => {
|
|
248
|
-
// Find the category for this case
|
|
249
|
-
const caseCategory = existingCategories.find(
|
|
250
|
-
(cat) => cat.uuid === case_.category_uuid
|
|
251
|
-
);
|
|
252
|
-
|
|
253
|
-
// Match by operator type and category name
|
|
254
|
-
return (
|
|
255
|
-
case_.type === rule.operator &&
|
|
256
|
-
caseCategory?.name.toLowerCase() === categoryKey
|
|
257
|
-
);
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const caseUuid = existingCase?.uuid || generateUUID();
|
|
262
|
-
|
|
263
|
-
// Parse rule value based on operator configuration
|
|
264
|
-
const operatorConfig = getOperatorConfig(rule.operator);
|
|
265
|
-
let arguments_: string[] = [];
|
|
266
|
-
|
|
267
|
-
if (operatorConfig) {
|
|
268
|
-
if (operatorConfig.operands === 0) {
|
|
269
|
-
// No operands needed
|
|
270
|
-
arguments_ = [];
|
|
271
|
-
} else if (operatorConfig.operands === 2) {
|
|
272
|
-
// Split value for two operands (e.g., "1 10" for between)
|
|
273
|
-
arguments_ = rule.value.split(' ').filter((arg: string) => arg.trim());
|
|
274
|
-
} else {
|
|
275
|
-
// Single operand - but split words for operators that expect multiple words
|
|
276
|
-
if (rule.value && rule.value.trim()) {
|
|
277
|
-
// Split on spaces and filter out empty strings
|
|
278
|
-
arguments_ = rule.value
|
|
279
|
-
.trim()
|
|
280
|
-
.split(/\s+/)
|
|
281
|
-
.filter((arg: string) => arg.length > 0);
|
|
282
|
-
} else {
|
|
283
|
-
arguments_ = [];
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
} else {
|
|
287
|
-
// Fallback for unknown operators - split on spaces if value exists
|
|
288
|
-
if (rule.value && rule.value.trim()) {
|
|
289
|
-
arguments_ = rule.value
|
|
290
|
-
.trim()
|
|
291
|
-
.split(/\s+/)
|
|
292
|
-
.filter((arg: string) => arg.length > 0);
|
|
293
|
-
} else {
|
|
294
|
-
arguments_ = [];
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
cases.push({
|
|
299
|
-
uuid: caseUuid,
|
|
300
|
-
type: rule.operator,
|
|
301
|
-
arguments: arguments_,
|
|
302
|
-
category_uuid: categoryInfo.uuid
|
|
303
|
-
});
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
// Add default category (always present)
|
|
307
|
-
// Name is "Other" if there are user rules, "All Responses" if there are no user rules
|
|
308
|
-
const defaultCategoryName = userRules.length > 0 ? 'Other' : 'All Responses';
|
|
309
|
-
|
|
310
|
-
// Try to find existing default category by name (prefer exact match)
|
|
311
|
-
let existingDefaultCategory = existingCategories.find(
|
|
312
|
-
(cat) => cat.name === defaultCategoryName
|
|
313
|
-
);
|
|
314
|
-
|
|
315
|
-
// If no exact match, try to find the other possible default category name
|
|
316
|
-
if (!existingDefaultCategory) {
|
|
317
|
-
const alternateName = userRules.length > 0 ? 'All Responses' : 'Other';
|
|
318
|
-
existingDefaultCategory = existingCategories.find(
|
|
319
|
-
(cat) => cat.name === alternateName
|
|
320
|
-
);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
const existingDefaultExit = existingDefaultCategory
|
|
324
|
-
? existingExits.find(
|
|
325
|
-
(exit) => exit.uuid === existingDefaultCategory.exit_uuid
|
|
326
|
-
)
|
|
327
|
-
: null;
|
|
328
|
-
|
|
329
|
-
const defaultExitUuid = existingDefaultExit?.uuid || generateUUID();
|
|
330
|
-
const defaultCategoryUuid = existingDefaultCategory?.uuid || generateUUID();
|
|
331
|
-
|
|
332
|
-
categories.push({
|
|
333
|
-
uuid: defaultCategoryUuid,
|
|
334
|
-
name: defaultCategoryName,
|
|
335
|
-
exit_uuid: defaultExitUuid
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
exits.push({
|
|
339
|
-
uuid: defaultExitUuid,
|
|
340
|
-
destination_uuid: existingDefaultExit?.destination_uuid || null
|
|
341
|
-
});
|
|
342
|
-
|
|
343
58
|
// Add "No Response" category last (if it exists in the original)
|
|
344
59
|
const existingNoResponseCategory = existingCategories.find(
|
|
345
60
|
(cat) => cat.name === 'No Response' || cat.name === 'Timeout'
|
|
@@ -351,253 +66,28 @@ const createWaitForResponseRouter = (
|
|
|
351
66
|
);
|
|
352
67
|
|
|
353
68
|
if (existingNoResponseExit) {
|
|
354
|
-
categories.push(existingNoResponseCategory);
|
|
355
|
-
exits.push(
|
|
69
|
+
router.categories.push(existingNoResponseCategory);
|
|
70
|
+
exits.push({
|
|
71
|
+
uuid: existingNoResponseExit.uuid,
|
|
72
|
+
destination_uuid: existingNoResponseExit.destination_uuid || null
|
|
73
|
+
});
|
|
356
74
|
}
|
|
357
75
|
}
|
|
358
76
|
|
|
359
|
-
|
|
360
|
-
const defaultCategory = categories.find(
|
|
361
|
-
(cat) => cat.name === 'Other' || cat.name === 'All Responses'
|
|
362
|
-
);
|
|
363
|
-
|
|
364
|
-
return {
|
|
365
|
-
router: {
|
|
366
|
-
type: 'switch' as const,
|
|
367
|
-
categories: categories,
|
|
368
|
-
default_category_uuid: defaultCategory?.uuid,
|
|
369
|
-
operand: '@input.text',
|
|
370
|
-
cases: cases
|
|
371
|
-
},
|
|
372
|
-
exits: exits
|
|
373
|
-
};
|
|
77
|
+
return { router, exits };
|
|
374
78
|
};
|
|
375
79
|
|
|
376
80
|
export const wait_for_response: NodeConfig = {
|
|
377
81
|
type: 'wait_for_response',
|
|
378
82
|
name: 'Wait for Response',
|
|
379
|
-
|
|
83
|
+
group: SPLIT_GROUPS.wait,
|
|
84
|
+
flowTypes: [FlowTypes.MESSAGE],
|
|
380
85
|
dialogSize: 'large',
|
|
381
86
|
form: {
|
|
382
|
-
rules:
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
minItems: 0,
|
|
387
|
-
maxItems: 100,
|
|
388
|
-
sortable: true,
|
|
389
|
-
maintainEmptyItem: true, // Explicitly enable empty item maintenance
|
|
390
|
-
isEmptyItem: (item: any) => {
|
|
391
|
-
// Helper function to get operator value from various formats
|
|
392
|
-
const getOperatorValue = (operator: any): string => {
|
|
393
|
-
if (typeof operator === 'string') {
|
|
394
|
-
return operator.trim();
|
|
395
|
-
} else if (Array.isArray(operator) && operator.length > 0) {
|
|
396
|
-
// Handle array format: [{value: "has_any_word", name: "..."}]
|
|
397
|
-
const firstOperator = operator[0];
|
|
398
|
-
if (
|
|
399
|
-
firstOperator &&
|
|
400
|
-
typeof firstOperator === 'object' &&
|
|
401
|
-
firstOperator.value
|
|
402
|
-
) {
|
|
403
|
-
return firstOperator.value.trim();
|
|
404
|
-
}
|
|
405
|
-
} else if (
|
|
406
|
-
operator &&
|
|
407
|
-
typeof operator === 'object' &&
|
|
408
|
-
operator.value
|
|
409
|
-
) {
|
|
410
|
-
// Handle object format: {value: "has_any_word", name: "..."}
|
|
411
|
-
return operator.value.trim();
|
|
412
|
-
}
|
|
413
|
-
return '';
|
|
414
|
-
};
|
|
415
|
-
|
|
416
|
-
// Check if operator and category are provided
|
|
417
|
-
const operatorValue = getOperatorValue(item.operator);
|
|
418
|
-
if (!operatorValue || !item.category || item.category.trim() === '') {
|
|
419
|
-
return true;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// Check if value is required based on operator configuration
|
|
423
|
-
const operatorConfig = getOperatorConfig(operatorValue);
|
|
424
|
-
if (operatorConfig && operatorConfig.operands === 1) {
|
|
425
|
-
// value1 is required for this operator
|
|
426
|
-
return !item.value1 || item.value1.trim() === '';
|
|
427
|
-
} else if (operatorConfig && operatorConfig.operands === 2) {
|
|
428
|
-
// Both value1 and value2 are required for this operator
|
|
429
|
-
return (
|
|
430
|
-
!item.value1 ||
|
|
431
|
-
item.value1.trim() === '' ||
|
|
432
|
-
!item.value2 ||
|
|
433
|
-
item.value2.trim() === ''
|
|
434
|
-
);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// No value required for this operator
|
|
438
|
-
return false;
|
|
439
|
-
},
|
|
440
|
-
onItemChange: (
|
|
441
|
-
itemIndex: number,
|
|
442
|
-
field: string,
|
|
443
|
-
value: any,
|
|
444
|
-
allItems: any[]
|
|
445
|
-
) => {
|
|
446
|
-
const updatedItems = [...allItems];
|
|
447
|
-
const item = { ...updatedItems[itemIndex] };
|
|
448
|
-
|
|
449
|
-
// Helper to get operator value from various formats
|
|
450
|
-
const getOperatorValue = (operator: any): string => {
|
|
451
|
-
if (typeof operator === 'string') {
|
|
452
|
-
return operator.trim();
|
|
453
|
-
} else if (Array.isArray(operator) && operator.length > 0) {
|
|
454
|
-
const firstOperator = operator[0];
|
|
455
|
-
if (
|
|
456
|
-
firstOperator &&
|
|
457
|
-
typeof firstOperator === 'object' &&
|
|
458
|
-
firstOperator.value
|
|
459
|
-
) {
|
|
460
|
-
return firstOperator.value.trim();
|
|
461
|
-
}
|
|
462
|
-
} else if (
|
|
463
|
-
operator &&
|
|
464
|
-
typeof operator === 'object' &&
|
|
465
|
-
operator.value
|
|
466
|
-
) {
|
|
467
|
-
return operator.value.trim();
|
|
468
|
-
}
|
|
469
|
-
return '';
|
|
470
|
-
};
|
|
471
|
-
|
|
472
|
-
// Update the changed field
|
|
473
|
-
item[field] = value;
|
|
474
|
-
|
|
475
|
-
// Get operator values (before and after the change)
|
|
476
|
-
const oldItem = allItems[itemIndex] || {};
|
|
477
|
-
const oldOperatorValue =
|
|
478
|
-
field === 'operator'
|
|
479
|
-
? getOperatorValue(oldItem.operator)
|
|
480
|
-
: getOperatorValue(item.operator);
|
|
481
|
-
const newOperatorValue = getOperatorValue(item.operator);
|
|
482
|
-
|
|
483
|
-
// Calculate what the default category name should be before the change
|
|
484
|
-
const oldDefaultCategory = generateDefaultCategoryName(
|
|
485
|
-
oldOperatorValue,
|
|
486
|
-
field === 'value1' ? oldItem.value1 : item.value1,
|
|
487
|
-
field === 'value2' ? oldItem.value2 : item.value2
|
|
488
|
-
);
|
|
489
|
-
|
|
490
|
-
// Calculate what the new default category name should be after the change
|
|
491
|
-
const newDefaultCategory = generateDefaultCategoryName(
|
|
492
|
-
newOperatorValue,
|
|
493
|
-
item.value1,
|
|
494
|
-
item.value2
|
|
495
|
-
);
|
|
496
|
-
|
|
497
|
-
// Determine if we should auto-update the category
|
|
498
|
-
const shouldUpdateCategory =
|
|
499
|
-
// Category is empty
|
|
500
|
-
!item.category ||
|
|
501
|
-
item.category.trim() === '' ||
|
|
502
|
-
// Category matches the old default (user hasn't customized it)
|
|
503
|
-
item.category === oldDefaultCategory;
|
|
504
|
-
|
|
505
|
-
// Auto-populate or update category if conditions are met
|
|
506
|
-
if (shouldUpdateCategory && newDefaultCategory) {
|
|
507
|
-
item.category = newDefaultCategory;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
updatedItems[itemIndex] = item;
|
|
511
|
-
return updatedItems;
|
|
512
|
-
},
|
|
513
|
-
itemConfig: {
|
|
514
|
-
operator: {
|
|
515
|
-
type: 'select',
|
|
516
|
-
required: true,
|
|
517
|
-
multi: false, // Explicitly set as single-select
|
|
518
|
-
options: operatorsToSelectOptions(getWaitForResponseOperators()),
|
|
519
|
-
flavor: 'xsmall',
|
|
520
|
-
width: '200px'
|
|
521
|
-
},
|
|
522
|
-
value1: {
|
|
523
|
-
type: 'text',
|
|
524
|
-
flavor: 'xsmall',
|
|
525
|
-
conditions: {
|
|
526
|
-
visible: (formData: Record<string, any>) => {
|
|
527
|
-
// Helper function to get operator value from various formats
|
|
528
|
-
const getOperatorValue = (operator: any): string => {
|
|
529
|
-
if (typeof operator === 'string') {
|
|
530
|
-
return operator.trim();
|
|
531
|
-
} else if (Array.isArray(operator) && operator.length > 0) {
|
|
532
|
-
const firstOperator = operator[0];
|
|
533
|
-
if (
|
|
534
|
-
firstOperator &&
|
|
535
|
-
typeof firstOperator === 'object' &&
|
|
536
|
-
firstOperator.value
|
|
537
|
-
) {
|
|
538
|
-
return firstOperator.value.trim();
|
|
539
|
-
}
|
|
540
|
-
} else if (
|
|
541
|
-
operator &&
|
|
542
|
-
typeof operator === 'object' &&
|
|
543
|
-
operator.value
|
|
544
|
-
) {
|
|
545
|
-
return operator.value.trim();
|
|
546
|
-
}
|
|
547
|
-
return '';
|
|
548
|
-
};
|
|
549
|
-
|
|
550
|
-
// Show value1 field for operators that require 1 or 2 operands
|
|
551
|
-
const operatorValue = getOperatorValue(formData.operator);
|
|
552
|
-
const operatorConfig = getOperatorConfig(operatorValue);
|
|
553
|
-
return operatorConfig ? operatorConfig.operands >= 1 : true;
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
},
|
|
557
|
-
value2: {
|
|
558
|
-
type: 'text',
|
|
559
|
-
flavor: 'xsmall',
|
|
560
|
-
conditions: {
|
|
561
|
-
visible: (formData: Record<string, any>) => {
|
|
562
|
-
// Helper function to get operator value from various formats
|
|
563
|
-
const getOperatorValue = (operator: any): string => {
|
|
564
|
-
if (typeof operator === 'string') {
|
|
565
|
-
return operator.trim();
|
|
566
|
-
} else if (Array.isArray(operator) && operator.length > 0) {
|
|
567
|
-
const firstOperator = operator[0];
|
|
568
|
-
if (
|
|
569
|
-
firstOperator &&
|
|
570
|
-
typeof firstOperator === 'object' &&
|
|
571
|
-
firstOperator.value
|
|
572
|
-
) {
|
|
573
|
-
return firstOperator.value.trim();
|
|
574
|
-
}
|
|
575
|
-
} else if (
|
|
576
|
-
operator &&
|
|
577
|
-
typeof operator === 'object' &&
|
|
578
|
-
operator.value
|
|
579
|
-
) {
|
|
580
|
-
return operator.value.trim();
|
|
581
|
-
}
|
|
582
|
-
return '';
|
|
583
|
-
};
|
|
584
|
-
|
|
585
|
-
// Show value2 field only if operator requires exactly 2 operands
|
|
586
|
-
const operatorValue = getOperatorValue(formData.operator);
|
|
587
|
-
const operatorConfig = getOperatorConfig(operatorValue);
|
|
588
|
-
return operatorConfig ? operatorConfig.operands === 2 : false;
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
},
|
|
592
|
-
category: {
|
|
593
|
-
type: 'text',
|
|
594
|
-
placeholder: 'Category',
|
|
595
|
-
required: true,
|
|
596
|
-
maxWidth: '120px',
|
|
597
|
-
flavor: 'xsmall'
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
},
|
|
87
|
+
rules: createRulesArrayConfig(
|
|
88
|
+
operatorsToSelectOptions(getWaitForResponseOperators()),
|
|
89
|
+
'Define rules to categorize responses'
|
|
90
|
+
),
|
|
601
91
|
timeout_enabled: {
|
|
602
92
|
type: 'checkbox',
|
|
603
93
|
label: (formData: Record<string, any>) => {
|
|
@@ -611,21 +101,17 @@ export const wait_for_response: NodeConfig = {
|
|
|
611
101
|
type: 'select',
|
|
612
102
|
placeholder: '5 minutes',
|
|
613
103
|
multi: false,
|
|
614
|
-
maxWidth: '
|
|
104
|
+
maxWidth: '100px',
|
|
615
105
|
flavor: 'xsmall',
|
|
616
106
|
options: TIMEOUT_OPTIONS,
|
|
107
|
+
|
|
617
108
|
conditions: {
|
|
618
109
|
visible: (formData: Record<string, any>) => {
|
|
619
110
|
return formData.timeout_enabled === true;
|
|
620
111
|
}
|
|
621
112
|
}
|
|
622
113
|
},
|
|
623
|
-
result_name:
|
|
624
|
-
type: 'text',
|
|
625
|
-
label: 'Result Name',
|
|
626
|
-
helpText: 'The name to save the response as',
|
|
627
|
-
placeholder: 'response'
|
|
628
|
-
}
|
|
114
|
+
result_name: resultNameField
|
|
629
115
|
},
|
|
630
116
|
layout: ['rules', 'result_name'],
|
|
631
117
|
gutter: [
|
|
@@ -635,7 +121,7 @@ export const wait_for_response: NodeConfig = {
|
|
|
635
121
|
gap: '0.5rem'
|
|
636
122
|
}
|
|
637
123
|
],
|
|
638
|
-
validate: (_formData:
|
|
124
|
+
validate: (_formData: FormData) => {
|
|
639
125
|
const errors: { [key: string]: string } = {};
|
|
640
126
|
|
|
641
127
|
// No validation needed - allow multiple rules to use same category name
|
|
@@ -647,52 +133,8 @@ export const wait_for_response: NodeConfig = {
|
|
|
647
133
|
};
|
|
648
134
|
},
|
|
649
135
|
toFormData: (node: Node) => {
|
|
650
|
-
// Extract rules from router cases
|
|
651
|
-
const rules =
|
|
652
|
-
if (node.router?.cases && node.router?.categories) {
|
|
653
|
-
node.router.cases.forEach((case_) => {
|
|
654
|
-
// Find the category for this case
|
|
655
|
-
const category = node.router!.categories.find(
|
|
656
|
-
(cat) => cat.uuid === case_.category_uuid
|
|
657
|
-
);
|
|
658
|
-
|
|
659
|
-
// Skip system categories
|
|
660
|
-
if (category && !isSystemCategory(category.name)) {
|
|
661
|
-
// Handle different operator types
|
|
662
|
-
const operatorConfig = getOperatorConfig(case_.type);
|
|
663
|
-
const operatorDisplayName = operatorConfig
|
|
664
|
-
? operatorConfig.name
|
|
665
|
-
: case_.type;
|
|
666
|
-
let value1 = '';
|
|
667
|
-
let value2 = '';
|
|
668
|
-
|
|
669
|
-
if (operatorConfig && operatorConfig.operands === 0) {
|
|
670
|
-
// No value needed for operators like has_text, has_number
|
|
671
|
-
value1 = '';
|
|
672
|
-
value2 = '';
|
|
673
|
-
} else if (operatorConfig && operatorConfig.operands === 1) {
|
|
674
|
-
// Single value for operators like has_number_lt - use value1
|
|
675
|
-
value1 = case_.arguments.join(' ');
|
|
676
|
-
value2 = '';
|
|
677
|
-
} else if (operatorConfig && operatorConfig.operands === 2) {
|
|
678
|
-
// Two separate values for operators like has_number_between
|
|
679
|
-
value1 = case_.arguments[0] || '';
|
|
680
|
-
value2 = case_.arguments[1] || '';
|
|
681
|
-
} else {
|
|
682
|
-
// Fallback: use first argument for unknown operators
|
|
683
|
-
value1 = case_.arguments.join(' ');
|
|
684
|
-
value2 = '';
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
rules.push({
|
|
688
|
-
operator: { value: case_.type, name: operatorDisplayName },
|
|
689
|
-
value1: value1,
|
|
690
|
-
value2: value2,
|
|
691
|
-
category: category.name
|
|
692
|
-
});
|
|
693
|
-
}
|
|
694
|
-
});
|
|
695
|
-
}
|
|
136
|
+
// Extract rules from router cases using shared function
|
|
137
|
+
const rules = casesToFormRules(node);
|
|
696
138
|
|
|
697
139
|
// Extract timeout configuration
|
|
698
140
|
const timeoutSeconds = node.router?.wait?.timeout?.seconds;
|
|
@@ -709,88 +151,12 @@ export const wait_for_response: NodeConfig = {
|
|
|
709
151
|
rules: rules,
|
|
710
152
|
timeout_enabled: !!timeoutSeconds,
|
|
711
153
|
timeout_duration: timeoutOption,
|
|
712
|
-
result_name: node.router?.result_name || '
|
|
154
|
+
result_name: node.router?.result_name || ''
|
|
713
155
|
};
|
|
714
156
|
},
|
|
715
|
-
fromFormData: (formData:
|
|
716
|
-
//
|
|
717
|
-
const
|
|
718
|
-
if (typeof operator === 'string') {
|
|
719
|
-
return operator.trim();
|
|
720
|
-
} else if (Array.isArray(operator) && operator.length > 0) {
|
|
721
|
-
// Handle array format: [{value: "has_any_word", name: "..."}]
|
|
722
|
-
const firstOperator = operator[0];
|
|
723
|
-
if (
|
|
724
|
-
firstOperator &&
|
|
725
|
-
typeof firstOperator === 'object' &&
|
|
726
|
-
firstOperator.value
|
|
727
|
-
) {
|
|
728
|
-
return firstOperator.value.trim();
|
|
729
|
-
}
|
|
730
|
-
} else if (operator && typeof operator === 'object' && operator.value) {
|
|
731
|
-
// Handle object format: {value: "has_any_word", name: "..."}
|
|
732
|
-
return operator.value.trim();
|
|
733
|
-
}
|
|
734
|
-
return '';
|
|
735
|
-
};
|
|
736
|
-
|
|
737
|
-
// Get user rules
|
|
738
|
-
const userRules = (formData.rules || [])
|
|
739
|
-
.filter((rule: any) => {
|
|
740
|
-
// Always need operator and category
|
|
741
|
-
const operatorValue = getOperatorValue(rule?.operator);
|
|
742
|
-
if (
|
|
743
|
-
!operatorValue ||
|
|
744
|
-
!rule?.category ||
|
|
745
|
-
operatorValue === '' ||
|
|
746
|
-
rule.category.trim() === ''
|
|
747
|
-
) {
|
|
748
|
-
return false;
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
// Check if value is required based on operator
|
|
752
|
-
const operatorConfig = getOperatorConfig(operatorValue);
|
|
753
|
-
if (operatorConfig && operatorConfig.operands === 1) {
|
|
754
|
-
// value1 is required for this operator
|
|
755
|
-
return rule?.value1 && rule.value1.trim() !== '';
|
|
756
|
-
} else if (operatorConfig && operatorConfig.operands === 2) {
|
|
757
|
-
// Both value1 and value2 are required for this operator
|
|
758
|
-
return (
|
|
759
|
-
rule?.value1 &&
|
|
760
|
-
rule.value1.trim() !== '' &&
|
|
761
|
-
rule?.value2 &&
|
|
762
|
-
rule.value2.trim() !== ''
|
|
763
|
-
);
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
// No value required for this operator
|
|
767
|
-
return true;
|
|
768
|
-
})
|
|
769
|
-
.map((rule: any) => {
|
|
770
|
-
const operatorValue = getOperatorValue(rule.operator);
|
|
771
|
-
const operatorConfig = getOperatorConfig(operatorValue);
|
|
772
|
-
|
|
773
|
-
let value = '';
|
|
774
|
-
|
|
775
|
-
if (operatorConfig && operatorConfig.operands === 1) {
|
|
776
|
-
// Single value from value1
|
|
777
|
-
value = rule.value1 ? rule.value1.trim() : '';
|
|
778
|
-
} else if (operatorConfig && operatorConfig.operands === 2) {
|
|
779
|
-
// Two values - combine them with space
|
|
780
|
-
const val1 = rule.value1 ? rule.value1.trim() : '';
|
|
781
|
-
const val2 = rule.value2 ? rule.value2.trim() : '';
|
|
782
|
-
value = `${val1} ${val2}`.trim();
|
|
783
|
-
} else {
|
|
784
|
-
// No value needed for 0-operand operators
|
|
785
|
-
value = '';
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
return {
|
|
789
|
-
operator: operatorValue,
|
|
790
|
-
value: value,
|
|
791
|
-
category: rule.category.trim()
|
|
792
|
-
};
|
|
793
|
-
});
|
|
157
|
+
fromFormData: (formData: FormData, originalNode: Node): Node => {
|
|
158
|
+
// Get user rules using shared extraction function
|
|
159
|
+
const userRules = extractUserRules(formData);
|
|
794
160
|
|
|
795
161
|
// If no user rules, clear cases but preserve other router config
|
|
796
162
|
if (userRules.length === 0) {
|
|
@@ -844,10 +210,14 @@ export const wait_for_response: NodeConfig = {
|
|
|
844
210
|
|
|
845
211
|
const router: any = {
|
|
846
212
|
...noRulesRouter,
|
|
847
|
-
result_name: formData.result_name || 'response',
|
|
848
213
|
cases: [] // Clear all cases when no rules
|
|
849
214
|
};
|
|
850
215
|
|
|
216
|
+
// Only set result_name if provided
|
|
217
|
+
if (formData.result_name && formData.result_name.trim() !== '') {
|
|
218
|
+
router.result_name = formData.result_name.trim();
|
|
219
|
+
}
|
|
220
|
+
|
|
851
221
|
// Build wait configuration based on form data
|
|
852
222
|
const waitConfig: any = {
|
|
853
223
|
type: 'msg'
|
|
@@ -923,10 +293,14 @@ export const wait_for_response: NodeConfig = {
|
|
|
923
293
|
|
|
924
294
|
// Build final router with wait configuration and result_name
|
|
925
295
|
const finalRouter: any = {
|
|
926
|
-
...router
|
|
927
|
-
result_name: formData.result_name || 'response'
|
|
296
|
+
...router
|
|
928
297
|
};
|
|
929
298
|
|
|
299
|
+
// Only set result_name if provided
|
|
300
|
+
if (formData.result_name && formData.result_name.trim() !== '') {
|
|
301
|
+
finalRouter.result_name = formData.result_name.trim();
|
|
302
|
+
}
|
|
303
|
+
|
|
930
304
|
// Build wait configuration based on form data
|
|
931
305
|
const waitConfig: any = {
|
|
932
306
|
type: 'msg'
|
|
@@ -1021,5 +395,10 @@ export const wait_for_response: NodeConfig = {
|
|
|
1021
395
|
router: finalRouter,
|
|
1022
396
|
exits: exits
|
|
1023
397
|
};
|
|
1024
|
-
}
|
|
398
|
+
},
|
|
399
|
+
|
|
400
|
+
// Localization support for categories
|
|
401
|
+
localizable: 'categories',
|
|
402
|
+
toLocalizationFormData: categoriesToLocalizationFormData,
|
|
403
|
+
fromLocalizationFormData: localizationFormDataToCategories
|
|
1025
404
|
};
|