@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
package/src/flow/CanvasNode.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { css, html, PropertyValueMap, TemplateResult } from 'lit';
|
|
2
2
|
import { repeat } from 'lit/directives/repeat.js';
|
|
3
3
|
import { ACTION_CONFIG, ActionConfig, NODE_CONFIG, NodeConfig } from './config';
|
|
4
|
+
import { ACTION_GROUP_METADATA, SPLIT_GROUP_METADATA } from './types';
|
|
4
5
|
import { Action, Exit, Node, NodeUI, Router } from '../store/flow-definition';
|
|
5
6
|
import { property } from 'lit/decorators.js';
|
|
6
7
|
import { RapidElement } from '../RapidElement';
|
|
@@ -8,6 +9,7 @@ import { getClasses } from '../utils';
|
|
|
8
9
|
import { Plumber } from './Plumber';
|
|
9
10
|
import { getStore } from '../store/Store';
|
|
10
11
|
import { CustomEventType } from '../interfaces';
|
|
12
|
+
import { AppState, fromStore, zustand } from '../store/AppState';
|
|
11
13
|
|
|
12
14
|
const DRAG_THRESHOLD = 5;
|
|
13
15
|
|
|
@@ -25,6 +27,22 @@ export class CanvasNode extends RapidElement {
|
|
|
25
27
|
@property({ type: Object })
|
|
26
28
|
private ui: NodeUI;
|
|
27
29
|
|
|
30
|
+
@fromStore(zustand, (state: AppState) => state.isTranslating)
|
|
31
|
+
private isTranslating!: boolean;
|
|
32
|
+
|
|
33
|
+
@fromStore(zustand, (state: AppState) => state.languageCode)
|
|
34
|
+
private languageCode!: string;
|
|
35
|
+
|
|
36
|
+
@fromStore(zustand, (state: AppState) => state.flowDefinition)
|
|
37
|
+
private flowDefinition!: any;
|
|
38
|
+
|
|
39
|
+
@fromStore(
|
|
40
|
+
zustand,
|
|
41
|
+
(state: AppState) =>
|
|
42
|
+
state.flowDefinition?._ui?.translation_filters?.categories || false
|
|
43
|
+
)
|
|
44
|
+
private includeCategoriesInTranslation!: boolean;
|
|
45
|
+
|
|
28
46
|
// Track exits that are in "removing" state
|
|
29
47
|
private exitRemovalTimeouts: Map<string, number> = new Map();
|
|
30
48
|
|
|
@@ -46,6 +64,18 @@ export class CanvasNode extends RapidElement {
|
|
|
46
64
|
private nodeClickStartPos: { x: number; y: number } | null = null;
|
|
47
65
|
private pendingNodeClick: { event: MouseEvent } | null = null;
|
|
48
66
|
|
|
67
|
+
// Track the height of the action being dragged (captured at drag start)
|
|
68
|
+
private draggedActionHeight: number = 0;
|
|
69
|
+
|
|
70
|
+
// Track external action drag (action being dragged from another node)
|
|
71
|
+
private externalDragInfo: {
|
|
72
|
+
action: Action;
|
|
73
|
+
sourceNodeUuid: string;
|
|
74
|
+
actionIndex: number;
|
|
75
|
+
dropIndex: number;
|
|
76
|
+
actionHeight: number;
|
|
77
|
+
} | null = null;
|
|
78
|
+
|
|
49
79
|
static get styles() {
|
|
50
80
|
return css`
|
|
51
81
|
|
|
@@ -147,6 +177,20 @@ export class CanvasNode extends RapidElement {
|
|
|
147
177
|
|
|
148
178
|
.node.execute-actions temba-sortable-list .action:last-child .body {
|
|
149
179
|
padding-bottom: 1.5em;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* Localization indicators */
|
|
183
|
+
.action.localizable:not(.has-localization) .action-content {
|
|
184
|
+
background: #fff8dc !important; /* Light yellow background for localizable but not yet localized */
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.non-localizable {
|
|
188
|
+
opacity: 0.25;
|
|
189
|
+
pointer-events: none;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.action.non-localizable .action-content {
|
|
193
|
+
cursor: not-allowed;
|
|
150
194
|
}
|
|
151
195
|
|
|
152
196
|
.action .drag-handle {
|
|
@@ -160,7 +204,7 @@ export class CanvasNode extends RapidElement {
|
|
|
160
204
|
pointer-events: auto; /* Ensure drag handle can receive events */
|
|
161
205
|
}
|
|
162
206
|
.title-spacer {
|
|
163
|
-
width:
|
|
207
|
+
width: 1.8em;
|
|
164
208
|
|
|
165
209
|
}
|
|
166
210
|
|
|
@@ -215,6 +259,10 @@ export class CanvasNode extends RapidElement {
|
|
|
215
259
|
margin: 0.2em;
|
|
216
260
|
}
|
|
217
261
|
|
|
262
|
+
.router-section {
|
|
263
|
+
/* Container for router and categories */
|
|
264
|
+
}
|
|
265
|
+
|
|
218
266
|
.categories {
|
|
219
267
|
display: flex;
|
|
220
268
|
flex-direction: row;
|
|
@@ -231,6 +279,11 @@ export class CanvasNode extends RapidElement {
|
|
|
231
279
|
flex-direction: column;
|
|
232
280
|
}
|
|
233
281
|
|
|
282
|
+
/* Localizable category - yellow background */
|
|
283
|
+
.category.localizable {
|
|
284
|
+
background-color: #fff8dc;
|
|
285
|
+
}
|
|
286
|
+
|
|
234
287
|
.action-exits {
|
|
235
288
|
padding-bottom: 0.7em;
|
|
236
289
|
margin-top: -0.7em;
|
|
@@ -339,12 +392,79 @@ export class CanvasNode extends RapidElement {
|
|
|
339
392
|
border-top-left-radius: var(--curvature);
|
|
340
393
|
border-top-right-radius: var(--curvature);
|
|
341
394
|
}
|
|
395
|
+
|
|
396
|
+
/* Add action button */
|
|
397
|
+
.add-action-button {
|
|
398
|
+
position: absolute;
|
|
399
|
+
bottom: 0.5em;
|
|
400
|
+
right: 0.5em;
|
|
401
|
+
width: 1.5em;
|
|
402
|
+
height: 1.5em;
|
|
403
|
+
border-radius: 50%;
|
|
404
|
+
background: var(--color-primary, #3b82f6);
|
|
405
|
+
color: white;
|
|
406
|
+
display: flex;
|
|
407
|
+
align-items: center;
|
|
408
|
+
justify-content: center;
|
|
409
|
+
cursor: pointer;
|
|
410
|
+
opacity: 0;
|
|
411
|
+
transition: opacity 200ms ease-in-out;
|
|
412
|
+
z-index: 10;
|
|
413
|
+
pointer-events: auto;
|
|
414
|
+
font-size: 0.9em;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.node.execute-actions:hover .add-action-button {
|
|
418
|
+
opacity: 0.8;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.add-action-button:hover {
|
|
422
|
+
opacity: 1 !important;
|
|
423
|
+
transform: scale(1.1);
|
|
424
|
+
}
|
|
342
425
|
}`;
|
|
343
426
|
}
|
|
344
427
|
|
|
345
428
|
constructor() {
|
|
346
429
|
super();
|
|
347
430
|
this.handleActionOrderChanged = this.handleActionOrderChanged.bind(this);
|
|
431
|
+
this.handleActionDragStart = this.handleActionDragStart.bind(this);
|
|
432
|
+
this.handleActionDragExternal = this.handleActionDragExternal.bind(this);
|
|
433
|
+
this.handleActionDragInternal = this.handleActionDragInternal.bind(this);
|
|
434
|
+
this.handleActionDragStop = this.handleActionDragStop.bind(this);
|
|
435
|
+
this.handleExternalActionDragOver =
|
|
436
|
+
this.handleExternalActionDragOver.bind(this);
|
|
437
|
+
this.handleExternalActionDrop = this.handleExternalActionDrop.bind(this);
|
|
438
|
+
this.handleExternalActionDragLeave =
|
|
439
|
+
this.handleExternalActionDragLeave.bind(this);
|
|
440
|
+
this.handleActionShowGhost = this.handleActionShowGhost.bind(this);
|
|
441
|
+
this.handleActionHideGhost = this.handleActionHideGhost.bind(this);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
connectedCallback() {
|
|
445
|
+
super.connectedCallback();
|
|
446
|
+
|
|
447
|
+
// Listen for external action drag events from Editor
|
|
448
|
+
this.addEventListener(
|
|
449
|
+
'action-drag-over',
|
|
450
|
+
this.handleExternalActionDragOver as EventListener
|
|
451
|
+
);
|
|
452
|
+
this.addEventListener(
|
|
453
|
+
'action-drop',
|
|
454
|
+
this.handleExternalActionDrop as EventListener
|
|
455
|
+
);
|
|
456
|
+
this.addEventListener(
|
|
457
|
+
'action-drag-leave',
|
|
458
|
+
this.handleExternalActionDragLeave as EventListener
|
|
459
|
+
);
|
|
460
|
+
this.addEventListener(
|
|
461
|
+
'action-show-ghost',
|
|
462
|
+
this.handleActionShowGhost as EventListener
|
|
463
|
+
);
|
|
464
|
+
this.addEventListener(
|
|
465
|
+
'action-hide-ghost',
|
|
466
|
+
this.handleActionHideGhost as EventListener
|
|
467
|
+
);
|
|
348
468
|
}
|
|
349
469
|
|
|
350
470
|
protected updated(
|
|
@@ -391,6 +511,28 @@ export class CanvasNode extends RapidElement {
|
|
|
391
511
|
// Remove the event listener when the component is removed
|
|
392
512
|
super.disconnectedCallback();
|
|
393
513
|
|
|
514
|
+
// Remove external drag event listeners
|
|
515
|
+
this.removeEventListener(
|
|
516
|
+
'action-drag-over',
|
|
517
|
+
this.handleExternalActionDragOver as EventListener
|
|
518
|
+
);
|
|
519
|
+
this.removeEventListener(
|
|
520
|
+
'action-drop',
|
|
521
|
+
this.handleExternalActionDrop as EventListener
|
|
522
|
+
);
|
|
523
|
+
this.removeEventListener(
|
|
524
|
+
'action-drag-leave',
|
|
525
|
+
this.handleExternalActionDragLeave as EventListener
|
|
526
|
+
);
|
|
527
|
+
this.removeEventListener(
|
|
528
|
+
'action-show-ghost',
|
|
529
|
+
this.handleActionShowGhost as EventListener
|
|
530
|
+
);
|
|
531
|
+
this.removeEventListener(
|
|
532
|
+
'action-hide-ghost',
|
|
533
|
+
this.handleActionHideGhost as EventListener
|
|
534
|
+
);
|
|
535
|
+
|
|
394
536
|
// Clear any pending exit removal timeouts
|
|
395
537
|
this.exitRemovalTimeouts.forEach((timeoutId) => {
|
|
396
538
|
clearTimeout(timeoutId);
|
|
@@ -615,6 +757,82 @@ export class CanvasNode extends RapidElement {
|
|
|
615
757
|
.updateNode(this.node.uuid, { ...this.node, actions: newActions });
|
|
616
758
|
}
|
|
617
759
|
|
|
760
|
+
private handleActionDragStart(event: CustomEvent) {
|
|
761
|
+
// Capture the height of the action being dragged
|
|
762
|
+
const actionId = event.detail.id;
|
|
763
|
+
const actionElement = this.querySelector(`#${actionId}`) as HTMLElement;
|
|
764
|
+
|
|
765
|
+
if (actionElement) {
|
|
766
|
+
const rect = actionElement.getBoundingClientRect();
|
|
767
|
+
this.draggedActionHeight = rect.height;
|
|
768
|
+
} else {
|
|
769
|
+
// Fallback to a reasonable default
|
|
770
|
+
this.draggedActionHeight = 60;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
private handleActionDragExternal(event: CustomEvent) {
|
|
775
|
+
// stop propagation of the original event from SortableList
|
|
776
|
+
event.stopPropagation();
|
|
777
|
+
|
|
778
|
+
// get the action being dragged
|
|
779
|
+
const actionId = event.detail.id;
|
|
780
|
+
const splitId = actionId.split('-');
|
|
781
|
+
if (splitId.length < 2 || isNaN(parseInt(splitId[1], 10))) {
|
|
782
|
+
// invalid format, do not proceed
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
const actionIndex = parseInt(splitId[1], 10);
|
|
786
|
+
const action = this.node.actions[actionIndex];
|
|
787
|
+
|
|
788
|
+
// fire event to editor to show canvas drop preview, including the captured height
|
|
789
|
+
this.fireCustomEvent(CustomEventType.DragExternal, {
|
|
790
|
+
action,
|
|
791
|
+
nodeUuid: this.node.uuid,
|
|
792
|
+
actionIndex,
|
|
793
|
+
mouseX: event.detail.mouseX,
|
|
794
|
+
mouseY: event.detail.mouseY,
|
|
795
|
+
actionHeight: this.draggedActionHeight
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
private handleActionDragInternal(_event: CustomEvent) {
|
|
800
|
+
// stop propagation of the original event from SortableList
|
|
801
|
+
_event.stopPropagation();
|
|
802
|
+
|
|
803
|
+
// fire event to editor to hide canvas drop preview
|
|
804
|
+
this.fireCustomEvent(CustomEventType.DragInternal, {});
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
private handleActionDragStop(event: CustomEvent) {
|
|
808
|
+
const isExternal = event.detail.isExternal;
|
|
809
|
+
|
|
810
|
+
if (isExternal) {
|
|
811
|
+
// stop propagation of the original event from SortableList
|
|
812
|
+
event.stopPropagation();
|
|
813
|
+
|
|
814
|
+
// get the action being dragged
|
|
815
|
+
const actionId = event.detail.id;
|
|
816
|
+
const split = actionId.split('-');
|
|
817
|
+
if (split.length < 2 || isNaN(Number(split[1]))) {
|
|
818
|
+
// invalid actionId format, do not proceed
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
const actionIndex = parseInt(split[1], 10);
|
|
822
|
+
const action = this.node.actions[actionIndex];
|
|
823
|
+
|
|
824
|
+
// fire event to editor to create new node
|
|
825
|
+
this.fireCustomEvent(CustomEventType.DragStop, {
|
|
826
|
+
action,
|
|
827
|
+
nodeUuid: this.node.uuid,
|
|
828
|
+
actionIndex,
|
|
829
|
+
isExternal: true,
|
|
830
|
+
mouseX: event.detail.mouseX,
|
|
831
|
+
mouseY: event.detail.mouseY
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
618
836
|
private handleActionMouseDown(event: MouseEvent, action: Action): void {
|
|
619
837
|
// Don't handle clicks on the remove button, drag handle, or when action is in removing state
|
|
620
838
|
const target = event.target as HTMLElement;
|
|
@@ -813,65 +1031,316 @@ export class CanvasNode extends RapidElement {
|
|
|
813
1031
|
this.pendingNodeClick = null;
|
|
814
1032
|
}
|
|
815
1033
|
|
|
1034
|
+
private handleAddActionClick(event: MouseEvent): void {
|
|
1035
|
+
event.preventDefault();
|
|
1036
|
+
event.stopPropagation();
|
|
1037
|
+
|
|
1038
|
+
// Fire event to request adding a new action to this node
|
|
1039
|
+
this.fireCustomEvent(CustomEventType.AddActionRequested, {
|
|
1040
|
+
nodeUuid: this.node.uuid
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
private calculateDropIndex(mouseY: number): number {
|
|
1045
|
+
// Get the sortable list element
|
|
1046
|
+
const sortableList = this.querySelector('temba-sortable-list');
|
|
1047
|
+
if (!sortableList || !this.node.actions)
|
|
1048
|
+
return this.node.actions?.length ?? 0;
|
|
1049
|
+
|
|
1050
|
+
// Get all action elements
|
|
1051
|
+
const actionElements = Array.from(
|
|
1052
|
+
sortableList.querySelectorAll('.action.sortable')
|
|
1053
|
+
);
|
|
1054
|
+
|
|
1055
|
+
if (actionElements.length === 0) {
|
|
1056
|
+
return 0;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
// Find where to insert based on mouse Y position
|
|
1060
|
+
for (let i = 0; i < actionElements.length; i++) {
|
|
1061
|
+
const actionElement = actionElements[i] as HTMLElement;
|
|
1062
|
+
const rect = actionElement.getBoundingClientRect();
|
|
1063
|
+
const centerY = rect.top + rect.height / 2;
|
|
1064
|
+
|
|
1065
|
+
if (mouseY < centerY) {
|
|
1066
|
+
return i;
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
// If past all elements, insert at the end
|
|
1071
|
+
return actionElements.length;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
private handleExternalActionDragOver(event: CustomEvent): void {
|
|
1075
|
+
// Only handle if this is an execute_actions node
|
|
1076
|
+
if (this.ui.type !== 'execute_actions') return;
|
|
1077
|
+
|
|
1078
|
+
const { action, sourceNodeUuid, actionIndex, mouseY, actionHeight } =
|
|
1079
|
+
event.detail;
|
|
1080
|
+
|
|
1081
|
+
// Don't accept drops from the same node
|
|
1082
|
+
if (sourceNodeUuid === this.node.uuid) return;
|
|
1083
|
+
|
|
1084
|
+
// Calculate where to drop
|
|
1085
|
+
const dropIndex = this.calculateDropIndex(mouseY);
|
|
1086
|
+
|
|
1087
|
+
// Store the drag info
|
|
1088
|
+
this.externalDragInfo = {
|
|
1089
|
+
action,
|
|
1090
|
+
sourceNodeUuid,
|
|
1091
|
+
actionIndex,
|
|
1092
|
+
dropIndex,
|
|
1093
|
+
actionHeight: actionHeight || 60 // fallback to 60px if not provided
|
|
1094
|
+
};
|
|
1095
|
+
|
|
1096
|
+
// Request update to show placeholder
|
|
1097
|
+
this.requestUpdate();
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
private handleExternalActionDragLeave(_event: CustomEvent): void {
|
|
1101
|
+
// Clear external drag state when drag leaves this node
|
|
1102
|
+
this.externalDragInfo = null;
|
|
1103
|
+
this.requestUpdate();
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
private handleActionShowGhost(_event: CustomEvent): void {
|
|
1107
|
+
// Show the ghost element in the sortable list
|
|
1108
|
+
const sortableList = this.querySelector('temba-sortable-list');
|
|
1109
|
+
if (sortableList) {
|
|
1110
|
+
const ghostElement = document.querySelector('.ghost') as HTMLElement;
|
|
1111
|
+
if (ghostElement) {
|
|
1112
|
+
ghostElement.style.display = 'block';
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
private handleActionHideGhost(_event: CustomEvent): void {
|
|
1118
|
+
// Hide the ghost element in the sortable list
|
|
1119
|
+
const sortableList = this.querySelector('temba-sortable-list');
|
|
1120
|
+
if (sortableList) {
|
|
1121
|
+
const ghostElement = document.querySelector('.ghost') as HTMLElement;
|
|
1122
|
+
if (ghostElement) {
|
|
1123
|
+
ghostElement.style.display = 'none';
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
private handleExternalActionDrop(event: CustomEvent): void {
|
|
1129
|
+
// Only handle if this is an execute_actions node
|
|
1130
|
+
if (this.ui.type !== 'execute_actions') return;
|
|
1131
|
+
|
|
1132
|
+
const { action, sourceNodeUuid, actionIndex } = event.detail;
|
|
1133
|
+
|
|
1134
|
+
// Don't accept drops from the same node
|
|
1135
|
+
if (sourceNodeUuid === this.node.uuid) return;
|
|
1136
|
+
|
|
1137
|
+
// Get the drop index from our tracking state
|
|
1138
|
+
const dropIndex =
|
|
1139
|
+
this.externalDragInfo?.dropIndex ?? this.node.actions?.length ?? 0;
|
|
1140
|
+
|
|
1141
|
+
// Clear external drag state
|
|
1142
|
+
this.externalDragInfo = null;
|
|
1143
|
+
|
|
1144
|
+
// Remove the action from the source node
|
|
1145
|
+
const store = getStore();
|
|
1146
|
+
if (!store) return;
|
|
1147
|
+
|
|
1148
|
+
const flowDefinition = store.getState().flowDefinition;
|
|
1149
|
+
if (!flowDefinition) return;
|
|
1150
|
+
|
|
1151
|
+
const sourceNode = flowDefinition.nodes.find(
|
|
1152
|
+
(n) => n.uuid === sourceNodeUuid
|
|
1153
|
+
);
|
|
1154
|
+
|
|
1155
|
+
if (sourceNode) {
|
|
1156
|
+
const updatedSourceActions = sourceNode.actions.filter(
|
|
1157
|
+
(_a, idx) => idx !== actionIndex
|
|
1158
|
+
);
|
|
1159
|
+
|
|
1160
|
+
// If source node has no actions left, remove it
|
|
1161
|
+
if (updatedSourceActions.length === 0) {
|
|
1162
|
+
this.fireCustomEvent(CustomEventType.NodeDeleted, {
|
|
1163
|
+
uuid: sourceNodeUuid
|
|
1164
|
+
});
|
|
1165
|
+
} else {
|
|
1166
|
+
// Update source node
|
|
1167
|
+
const updatedSourceNode = {
|
|
1168
|
+
...sourceNode,
|
|
1169
|
+
actions: updatedSourceActions
|
|
1170
|
+
};
|
|
1171
|
+
getStore()?.getState().updateNode(sourceNodeUuid, updatedSourceNode);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
// Add the action to this node at the calculated position
|
|
1176
|
+
const newActions = [...this.node.actions];
|
|
1177
|
+
newActions.splice(dropIndex, 0, action);
|
|
1178
|
+
|
|
1179
|
+
const updatedNode = { ...this.node, actions: newActions };
|
|
1180
|
+
getStore()?.getState().updateNode(this.node.uuid, updatedNode);
|
|
1181
|
+
|
|
1182
|
+
// Request update
|
|
1183
|
+
this.requestUpdate();
|
|
1184
|
+
}
|
|
1185
|
+
|
|
816
1186
|
private renderTitle(
|
|
817
1187
|
config: ActionConfig,
|
|
818
1188
|
action: Action,
|
|
819
1189
|
index: number,
|
|
820
1190
|
isRemoving: boolean = false
|
|
821
1191
|
) {
|
|
822
|
-
|
|
823
|
-
|
|
1192
|
+
const color = config.group
|
|
1193
|
+
? ACTION_GROUP_METADATA[config.group]?.color
|
|
1194
|
+
: '#aaaaaa';
|
|
1195
|
+
return html`<div class="cn-title" style="background:${color}">
|
|
1196
|
+
${!this.isTranslating && this.node?.actions?.length > 1
|
|
824
1197
|
? html`<temba-icon class="drag-handle" name="sort"></temba-icon>`
|
|
825
|
-
:
|
|
1198
|
+
: html`<div class="title-spacer"></div>`}
|
|
826
1199
|
|
|
827
1200
|
<div class="name">${isRemoving ? 'Remove?' : config.name}</div>
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
1201
|
+
${!this.isTranslating
|
|
1202
|
+
? html`<div
|
|
1203
|
+
class="remove-button"
|
|
1204
|
+
@click=${(e: MouseEvent) =>
|
|
1205
|
+
this.handleActionRemoveClick(e, action, index)}
|
|
1206
|
+
title="Remove action"
|
|
1207
|
+
>
|
|
1208
|
+
✕
|
|
1209
|
+
</div>`
|
|
1210
|
+
: html`<div class="title-spacer"></div>`}
|
|
836
1211
|
</div>`;
|
|
837
1212
|
}
|
|
838
1213
|
|
|
839
|
-
private renderNodeTitle(
|
|
1214
|
+
private renderNodeTitle(
|
|
1215
|
+
config: NodeConfig,
|
|
1216
|
+
node: Node,
|
|
1217
|
+
ui: NodeUI,
|
|
1218
|
+
isRemoving: boolean = false
|
|
1219
|
+
) {
|
|
1220
|
+
// Get color from the appropriate metadata (either ACTION or SPLIT)
|
|
1221
|
+
const color = config.group
|
|
1222
|
+
? ACTION_GROUP_METADATA[config.group]?.color ||
|
|
1223
|
+
SPLIT_GROUP_METADATA[config.group]?.color
|
|
1224
|
+
: '#aaaaaa';
|
|
840
1225
|
return html`<div
|
|
841
1226
|
class="cn-title ${isRemoving ? 'removing' : ''}"
|
|
842
|
-
style="background:${
|
|
1227
|
+
style="background:${color}"
|
|
843
1228
|
>
|
|
844
1229
|
<div class="title-spacer"></div>
|
|
845
|
-
<div class="name"
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
✕
|
|
1230
|
+
<div class="name">
|
|
1231
|
+
${isRemoving
|
|
1232
|
+
? 'Remove?'
|
|
1233
|
+
: config.renderTitle
|
|
1234
|
+
? config.renderTitle(node, ui)
|
|
1235
|
+
: html`${config.name}`}
|
|
852
1236
|
</div>
|
|
1237
|
+
${!this.isTranslating
|
|
1238
|
+
? html`<div
|
|
1239
|
+
class="remove-button"
|
|
1240
|
+
@click=${(e: MouseEvent) => this.handleNodeRemoveClick(e)}
|
|
1241
|
+
title="Remove node"
|
|
1242
|
+
>
|
|
1243
|
+
✕
|
|
1244
|
+
</div>`
|
|
1245
|
+
: html`<div class="title-spacer"></div>`}
|
|
853
1246
|
</div>`;
|
|
854
1247
|
}
|
|
855
1248
|
|
|
1249
|
+
private renderDropPlaceholder() {
|
|
1250
|
+
const height = this.externalDragInfo?.actionHeight || 60;
|
|
1251
|
+
return html`<div
|
|
1252
|
+
class="action sortable drop-placeholder"
|
|
1253
|
+
style="height: ${height}px; background: #f3f4f6; border: 2px dashed #d1d5db; border-radius: var(--curvature);"
|
|
1254
|
+
></div>`;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
/**
|
|
1258
|
+
* Get the localized version of an action if translating, otherwise return the original action.
|
|
1259
|
+
* Falls back to base language values if no localization exists for a field.
|
|
1260
|
+
*/
|
|
1261
|
+
private getLocalizedAction(action: Action): Action {
|
|
1262
|
+
// If not translating or no flow definition, return original action
|
|
1263
|
+
if (
|
|
1264
|
+
!this.isTranslating ||
|
|
1265
|
+
!this.flowDefinition ||
|
|
1266
|
+
!this.languageCode ||
|
|
1267
|
+
this.languageCode === this.flowDefinition.language
|
|
1268
|
+
) {
|
|
1269
|
+
return action;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
// Check if there's localization for this action
|
|
1273
|
+
const localization =
|
|
1274
|
+
this.flowDefinition?.localization?.[this.languageCode]?.[action.uuid];
|
|
1275
|
+
|
|
1276
|
+
if (!localization) {
|
|
1277
|
+
// No localization available, return original action
|
|
1278
|
+
return action;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
// Create a new action with localized values, falling back to base language
|
|
1282
|
+
const localizedAction = { ...action };
|
|
1283
|
+
|
|
1284
|
+
// Apply localized values for each field
|
|
1285
|
+
Object.keys(localization).forEach((field) => {
|
|
1286
|
+
const localizedValue = localization[field];
|
|
1287
|
+
if (Array.isArray(localizedValue)) {
|
|
1288
|
+
// Localized values are stored as arrays
|
|
1289
|
+
if (localizedValue.length > 0) {
|
|
1290
|
+
// For single-value fields like 'text', take the first element
|
|
1291
|
+
// For array fields like 'quick_replies', use the whole array
|
|
1292
|
+
if (Array.isArray(action[field])) {
|
|
1293
|
+
localizedAction[field] = localizedValue;
|
|
1294
|
+
} else {
|
|
1295
|
+
localizedAction[field] = localizedValue[0];
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
});
|
|
1300
|
+
|
|
1301
|
+
return localizedAction;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
856
1304
|
private renderAction(node: Node, action: Action, index: number) {
|
|
857
1305
|
const config = ACTION_CONFIG[action.type];
|
|
858
1306
|
const isRemoving = this.actionRemovingState.has(action.uuid);
|
|
1307
|
+
const isLocalizable = config?.localizable && config.localizable.length > 0;
|
|
1308
|
+
const isDisabled = this.isTranslating && !isLocalizable;
|
|
1309
|
+
|
|
1310
|
+
// Check if this action has localization data
|
|
1311
|
+
const hasLocalization =
|
|
1312
|
+
this.isTranslating &&
|
|
1313
|
+
this.flowDefinition?.localization?.[this.languageCode]?.[action.uuid];
|
|
1314
|
+
|
|
1315
|
+
// Get the localized action if translating
|
|
1316
|
+
const displayAction = this.getLocalizedAction(action);
|
|
859
1317
|
|
|
860
1318
|
if (config) {
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
1319
|
+
const classes = [
|
|
1320
|
+
'action',
|
|
1321
|
+
'sortable',
|
|
1322
|
+
action.type,
|
|
1323
|
+
isRemoving ? 'removing' : '',
|
|
1324
|
+
isLocalizable && this.isTranslating ? 'localizable' : '',
|
|
1325
|
+
hasLocalization ? 'has-localization' : '',
|
|
1326
|
+
isDisabled ? 'non-localizable' : ''
|
|
1327
|
+
]
|
|
1328
|
+
.filter(Boolean)
|
|
1329
|
+
.join(' ');
|
|
1330
|
+
|
|
1331
|
+
return html`<div class="${classes}" id="action-${index}">
|
|
865
1332
|
<div
|
|
866
1333
|
class="action-content"
|
|
867
|
-
@mousedown=${(e: MouseEvent) =>
|
|
868
|
-
|
|
869
|
-
|
|
1334
|
+
@mousedown=${(e: MouseEvent) =>
|
|
1335
|
+
!isDisabled && this.handleActionMouseDown(e, action)}
|
|
1336
|
+
@mouseup=${(e: MouseEvent) =>
|
|
1337
|
+
!isDisabled && this.handleActionMouseUp(e, action)}
|
|
1338
|
+
style="cursor: ${isDisabled ? 'not-allowed' : 'pointer'}"
|
|
870
1339
|
>
|
|
871
1340
|
${this.renderTitle(config, action, index, isRemoving)}
|
|
872
1341
|
<div class="body">
|
|
873
1342
|
${config.render
|
|
874
|
-
? config.render(node,
|
|
1343
|
+
? config.render(node, displayAction)
|
|
875
1344
|
: html`<pre>${action.type}</pre>`}
|
|
876
1345
|
</div>
|
|
877
1346
|
</div>
|
|
@@ -893,6 +1362,31 @@ export class CanvasNode extends RapidElement {
|
|
|
893
1362
|
</div>`;
|
|
894
1363
|
}
|
|
895
1364
|
|
|
1365
|
+
private renderActionsWithPlaceholder() {
|
|
1366
|
+
if (!this.externalDragInfo) {
|
|
1367
|
+
// No external drag, render normally
|
|
1368
|
+
return this.node.actions.map((action, index) =>
|
|
1369
|
+
this.renderAction(this.node, action, index)
|
|
1370
|
+
);
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
// Insert placeholder at the drop index
|
|
1374
|
+
const result = [];
|
|
1375
|
+
for (let i = 0; i < this.node.actions.length; i++) {
|
|
1376
|
+
if (i === this.externalDragInfo.dropIndex) {
|
|
1377
|
+
result.push(this.renderDropPlaceholder());
|
|
1378
|
+
}
|
|
1379
|
+
result.push(this.renderAction(this.node, this.node.actions[i], i));
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
// If dropping at the end, add placeholder after all actions
|
|
1383
|
+
if (this.externalDragInfo.dropIndex >= this.node.actions.length) {
|
|
1384
|
+
result.push(this.renderDropPlaceholder());
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
return result;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
896
1390
|
private renderRouter(router: Router, ui: NodeUI) {
|
|
897
1391
|
const nodeConfig = NODE_CONFIG[ui.type];
|
|
898
1392
|
if (nodeConfig) {
|
|
@@ -917,6 +1411,10 @@ export class CanvasNode extends RapidElement {
|
|
|
917
1411
|
return null;
|
|
918
1412
|
}
|
|
919
1413
|
|
|
1414
|
+
// Check if this node type supports category localization
|
|
1415
|
+
const nodeConfig = NODE_CONFIG[this.ui?.type];
|
|
1416
|
+
const supportsLocalization = nodeConfig?.localizable === 'categories';
|
|
1417
|
+
|
|
920
1418
|
return html`<div class="categories">
|
|
921
1419
|
${repeat(
|
|
922
1420
|
node.router.categories,
|
|
@@ -926,13 +1424,44 @@ export class CanvasNode extends RapidElement {
|
|
|
926
1424
|
(exit: Exit) => exit.uuid == category.exit_uuid
|
|
927
1425
|
);
|
|
928
1426
|
|
|
1427
|
+
// Get localized category name if translating
|
|
1428
|
+
let displayName = category.name;
|
|
1429
|
+
let isLocalized = false;
|
|
1430
|
+
|
|
1431
|
+
if (
|
|
1432
|
+
this.isTranslating &&
|
|
1433
|
+
this.languageCode !== 'eng' &&
|
|
1434
|
+
supportsLocalization
|
|
1435
|
+
) {
|
|
1436
|
+
const localization =
|
|
1437
|
+
this.flowDefinition?.localization?.[this.languageCode];
|
|
1438
|
+
if (localization && localization[category.uuid]) {
|
|
1439
|
+
const categoryLocalization = localization[category.uuid];
|
|
1440
|
+
if (categoryLocalization.name && categoryLocalization.name[0]) {
|
|
1441
|
+
displayName = categoryLocalization.name[0];
|
|
1442
|
+
isLocalized = true;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
// Category is localizable if: translating, supports localization, categories enabled, and not base language
|
|
1448
|
+
const isLocalizable =
|
|
1449
|
+
this.isTranslating &&
|
|
1450
|
+
this.languageCode !== 'eng' &&
|
|
1451
|
+
supportsLocalization &&
|
|
1452
|
+
this.includeCategoriesInTranslation &&
|
|
1453
|
+
!isLocalized;
|
|
1454
|
+
|
|
929
1455
|
return html`<div
|
|
930
|
-
class
|
|
1456
|
+
class=${getClasses({
|
|
1457
|
+
category: true,
|
|
1458
|
+
localizable: isLocalizable
|
|
1459
|
+
})}
|
|
931
1460
|
@mousedown=${(e: MouseEvent) => this.handleNodeMouseDown(e)}
|
|
932
1461
|
@mouseup=${(e: MouseEvent) => this.handleNodeMouseUp(e)}
|
|
933
1462
|
style="cursor: pointer;"
|
|
934
1463
|
>
|
|
935
|
-
<div class="cn-title">${
|
|
1464
|
+
<div class="cn-title">${displayName}</div>
|
|
936
1465
|
${this.renderExit(exit)}
|
|
937
1466
|
</div>`;
|
|
938
1467
|
}
|
|
@@ -961,12 +1490,21 @@ export class CanvasNode extends RapidElement {
|
|
|
961
1490
|
|
|
962
1491
|
const nodeConfig = NODE_CONFIG[this.ui.type];
|
|
963
1492
|
|
|
1493
|
+
// Check if this node should be disabled (grayed out)
|
|
1494
|
+
const supportsLocalization = nodeConfig?.localizable === 'categories';
|
|
1495
|
+
const isNodeDisabled =
|
|
1496
|
+
this.isTranslating &&
|
|
1497
|
+
supportsLocalization &&
|
|
1498
|
+
!this.includeCategoriesInTranslation;
|
|
1499
|
+
|
|
964
1500
|
return html`
|
|
965
1501
|
<div
|
|
966
1502
|
id="${this.node.uuid}"
|
|
967
|
-
class
|
|
968
|
-
|
|
969
|
-
: ''
|
|
1503
|
+
class=${getClasses({
|
|
1504
|
+
node: true,
|
|
1505
|
+
'execute-actions': this.ui.type === 'execute_actions',
|
|
1506
|
+
'non-localizable': isNodeDisabled
|
|
1507
|
+
})}
|
|
970
1508
|
style="left:${this.ui.position.left}px;top:${this.ui.position.top}px"
|
|
971
1509
|
>
|
|
972
1510
|
${nodeConfig && nodeConfig.type !== 'execute_actions'
|
|
@@ -978,28 +1516,37 @@ export class CanvasNode extends RapidElement {
|
|
|
978
1516
|
>
|
|
979
1517
|
${this.renderNodeTitle(
|
|
980
1518
|
nodeConfig,
|
|
1519
|
+
this.node,
|
|
1520
|
+
this.ui,
|
|
981
1521
|
this.actionRemovingState.has(this.node.uuid)
|
|
982
1522
|
)}
|
|
983
|
-
${nodeConfig.render
|
|
1523
|
+
${nodeConfig.render
|
|
1524
|
+
? nodeConfig.render(this.node, this.ui)
|
|
1525
|
+
: null}
|
|
984
1526
|
</div>
|
|
985
1527
|
</div>`
|
|
986
1528
|
: this.node.actions.length > 0
|
|
987
1529
|
? this.ui.type === 'execute_actions'
|
|
988
1530
|
? html`<temba-sortable-list
|
|
989
1531
|
dragHandle="drag-handle"
|
|
1532
|
+
externalDrag
|
|
990
1533
|
@temba-order-changed="${this.handleActionOrderChanged}"
|
|
1534
|
+
@temba-drag-start="${this.handleActionDragStart}"
|
|
1535
|
+
@temba-drag-external="${this.handleActionDragExternal}"
|
|
1536
|
+
@temba-drag-internal="${this.handleActionDragInternal}"
|
|
1537
|
+
@temba-drag-stop="${this.handleActionDragStop}"
|
|
991
1538
|
>
|
|
992
|
-
${this.
|
|
993
|
-
this.renderAction(this.node, action, index)
|
|
994
|
-
)}
|
|
1539
|
+
${this.renderActionsWithPlaceholder()}
|
|
995
1540
|
</temba-sortable-list>`
|
|
996
1541
|
: html`${this.node.actions.map((action, index) =>
|
|
997
1542
|
this.renderAction(this.node, action, index)
|
|
998
1543
|
)}`
|
|
999
1544
|
: ''}
|
|
1000
1545
|
${this.node.router
|
|
1001
|
-
? html
|
|
1002
|
-
|
|
1546
|
+
? html`<div class="router-section">
|
|
1547
|
+
${this.renderRouter(this.node.router, this.ui)}
|
|
1548
|
+
${this.renderCategories(this.node)}
|
|
1549
|
+
</div>`
|
|
1003
1550
|
: html`<div class="action-exits">
|
|
1004
1551
|
${repeat(
|
|
1005
1552
|
this.node.exits,
|
|
@@ -1007,6 +1554,15 @@ export class CanvasNode extends RapidElement {
|
|
|
1007
1554
|
(exit) => this.renderExit(exit)
|
|
1008
1555
|
)}
|
|
1009
1556
|
</div>`}
|
|
1557
|
+
${this.ui.type === 'execute_actions' && !this.isTranslating
|
|
1558
|
+
? html`<div
|
|
1559
|
+
class="add-action-button"
|
|
1560
|
+
@click=${(e: MouseEvent) => this.handleAddActionClick(e)}
|
|
1561
|
+
title="Add action"
|
|
1562
|
+
>
|
|
1563
|
+
<temba-icon name="add"></temba-icon>
|
|
1564
|
+
</div>`
|
|
1565
|
+
: ''}
|
|
1010
1566
|
</div>
|
|
1011
1567
|
`;
|
|
1012
1568
|
}
|