@nyaruka/temba-components 0.131.0 → 0.131.2
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 +67 -1
- package/demo/data/flows/food-order.json +2 -2
- package/demo/data/flows/sample-flow.json +74 -125
- package/dist/static/svg/index.svg +1 -1
- package/dist/temba-components.js +1156 -619
- 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/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 +327 -19
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +562 -66
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +240 -93
- package/out-tsc/src/flow/NodeEditor.js.map +1 -1
- package/out-tsc/src/flow/NodeTypeSelector.js +499 -0
- package/out-tsc/src/flow/NodeTypeSelector.js.map +1 -0
- package/out-tsc/src/flow/actions/add_contact_groups.js +3 -3
- package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
- package/out-tsc/src/flow/actions/add_contact_urn.js +62 -4
- package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
- package/out-tsc/src/flow/actions/add_input_labels.js +3 -3
- package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
- package/out-tsc/src/flow/actions/play_audio.js +2 -2
- package/out-tsc/src/flow/actions/play_audio.js.map +1 -1
- package/out-tsc/src/flow/actions/remove_contact_groups.js +6 -5
- package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -1
- package/out-tsc/src/flow/actions/request_optin.js +2 -2
- package/out-tsc/src/flow/actions/request_optin.js.map +1 -1
- package/out-tsc/src/flow/actions/say_msg.js +2 -2
- package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
- package/out-tsc/src/flow/actions/send_broadcast.js +76 -23
- package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
- package/out-tsc/src/flow/actions/send_email.js +4 -5
- package/out-tsc/src/flow/actions/send_email.js.map +1 -1
- package/out-tsc/src/flow/actions/send_msg.js +9 -19
- package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_channel.js +5 -9
- package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_field.js +19 -20
- package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_language.js +2 -2
- package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_name.js +2 -12
- package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_status.js +2 -2
- package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
- package/out-tsc/src/flow/actions/set_run_result.js +3 -3
- package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
- package/out-tsc/src/flow/actions/start_session.js +180 -6
- package/out-tsc/src/flow/actions/start_session.js.map +1 -1
- package/out-tsc/src/flow/config.js +11 -15
- 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 +17 -0
- package/out-tsc/src/flow/nodes/shared.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_airtime.js +205 -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 +147 -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 +68 -2
- package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_groups.js +12 -9
- package/out-tsc/src/flow/nodes/split_by_groups.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_intent.js +7 -0
- package/out-tsc/src/flow/nodes/split_by_intent.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_llm.js +3 -2
- package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +2 -2
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_random.js +3 -3
- package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_resthook.js +108 -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 +206 -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 +153 -2
- package/out-tsc/src/flow/nodes/split_by_scheme.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_subflow.js +6 -4
- package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_ticket.js +3 -2
- package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_webhook.js +3 -2
- package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_audio.js +2 -2
- package/out-tsc/src/flow/nodes/wait_for_audio.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_digits.js +2 -2
- package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_image.js +2 -2
- package/out-tsc/src/flow/nodes/wait_for_image.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_location.js +2 -2
- package/out-tsc/src/flow/nodes/wait_for_location.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_menu.js +2 -2
- package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js +32 -567
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_video.js +2 -2
- package/out-tsc/src/flow/nodes/wait_for_video.js.map +1 -1
- package/out-tsc/src/flow/types.js +71 -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/ContactSearch.js +1 -1
- package/out-tsc/src/form/ContactSearch.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/list/SortableList.js +98 -33
- package/out-tsc/src/list/SortableList.js.map +1 -1
- package/out-tsc/src/live/ContactChat.js +15 -18
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/src/store/AppState.js +53 -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 +4 -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-flow-editor-node.test.js +85 -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-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 +115 -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 +2 -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/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/events.ts +2 -6
- package/src/flow/CanvasMenu.ts +217 -0
- package/src/flow/CanvasNode.ts +408 -10
- package/src/flow/Editor.ts +683 -44
- package/src/flow/NodeEditor.ts +304 -125
- package/src/flow/NodeTypeSelector.ts +592 -0
- package/src/flow/actions/add_contact_groups.ts +4 -4
- package/src/flow/actions/add_contact_urn.ts +76 -4
- package/src/flow/actions/add_input_labels.ts +4 -4
- package/src/flow/actions/play_audio.ts +2 -2
- package/src/flow/actions/remove_contact_groups.ts +14 -6
- package/src/flow/actions/request_optin.ts +2 -2
- package/src/flow/actions/say_msg.ts +2 -2
- package/src/flow/actions/send_broadcast.ts +85 -23
- package/src/flow/actions/send_email.ts +10 -6
- package/src/flow/actions/send_msg.ts +22 -32
- package/src/flow/actions/set_contact_channel.ts +5 -11
- package/src/flow/actions/set_contact_field.ts +20 -25
- package/src/flow/actions/set_contact_language.ts +9 -4
- package/src/flow/actions/set_contact_name.ts +3 -15
- package/src/flow/actions/set_contact_status.ts +3 -3
- package/src/flow/actions/set_run_result.ts +4 -4
- package/src/flow/actions/start_session.ts +208 -6
- package/src/flow/config.ts +13 -15
- package/src/flow/currencies.ts +51 -0
- package/src/flow/nodes/shared-rules.ts +301 -0
- package/src/flow/nodes/shared.ts +18 -0
- package/src/flow/nodes/split_by_airtime.ts +238 -5
- package/src/flow/nodes/split_by_contact_field.ts +185 -3
- package/src/flow/nodes/split_by_expression.ts +94 -2
- package/src/flow/nodes/split_by_groups.ts +15 -10
- package/src/flow/nodes/split_by_intent.ts +7 -0
- package/src/flow/nodes/split_by_llm.ts +4 -3
- package/src/flow/nodes/split_by_llm_categorize.ts +4 -4
- package/src/flow/nodes/split_by_random.ts +5 -5
- package/src/flow/nodes/split_by_resthook.ts +130 -0
- package/src/flow/nodes/split_by_run_result.ts +249 -3
- package/src/flow/nodes/split_by_scheme.ts +192 -2
- package/src/flow/nodes/split_by_subflow.ts +6 -4
- package/src/flow/nodes/split_by_ticket.ts +4 -3
- package/src/flow/nodes/split_by_webhook.ts +6 -5
- package/src/flow/nodes/wait_for_audio.ts +2 -2
- package/src/flow/nodes/wait_for_digits.ts +2 -2
- package/src/flow/nodes/wait_for_image.ts +2 -2
- package/src/flow/nodes/wait_for_location.ts +2 -2
- package/src/flow/nodes/wait_for_menu.ts +2 -2
- package/src/flow/nodes/wait_for_response.ts +48 -679
- package/src/flow/nodes/wait_for_video.ts +2 -2
- package/src/flow/types.ts +109 -23
- package/src/flow/utils.ts +108 -14
- package/src/form/ContactSearch.ts +1 -1
- package/src/form/FieldRenderer.ts +2 -4
- package/src/interfaces.ts +3 -0
- package/src/list/SortableList.ts +109 -34
- package/src/live/ContactChat.ts +15 -18
- package/src/store/AppState.ts +69 -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/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 +4 -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-flow-editor-node.test.ts +102 -2
- package/test/temba-flow-editor.test.ts +7 -8
- package/test/temba-node-editor.test.ts +3 -1
- package/test/temba-node-type-selector.test.ts +152 -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 +2 -0
- package/test-assets/contacts/history.json +14 -20
- package/web-dev-server.config.mjs +3 -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/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/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TemplateResult } from 'lit-html';
|
|
2
|
-
import { Action, Node } from '../store/flow-definition';
|
|
2
|
+
import { Action, Node, NodeUI } from '../store/flow-definition';
|
|
3
3
|
|
|
4
4
|
export interface ValidationResult {
|
|
5
5
|
valid: boolean;
|
|
@@ -78,9 +78,10 @@ export interface FormConfig {
|
|
|
78
78
|
export interface NodeConfig extends FormConfig {
|
|
79
79
|
type: string;
|
|
80
80
|
name?: string;
|
|
81
|
-
|
|
81
|
+
group?: ActionGroup | SplitGroup; // Nodes can use either when showAsAction is true
|
|
82
82
|
dialogSize?: 'small' | 'medium' | 'large' | 'xlarge';
|
|
83
83
|
action?: ActionConfig;
|
|
84
|
+
showAsAction?: boolean; // if true, show in action dialog instead of splits (default: false - nodes show in splits)
|
|
84
85
|
router?: {
|
|
85
86
|
type: 'switch' | 'random';
|
|
86
87
|
defaultCategory?: string;
|
|
@@ -98,9 +99,11 @@ export interface NodeConfig extends FormConfig {
|
|
|
98
99
|
}[];
|
|
99
100
|
};
|
|
100
101
|
|
|
101
|
-
toFormData?: (node: Node) => FormData;
|
|
102
|
+
toFormData?: (node: Node, nodeUI?: any) => FormData;
|
|
102
103
|
fromFormData?: (formData: FormData, originalNode: Node) => Node;
|
|
103
|
-
|
|
104
|
+
toUIConfig?: (formData: FormData) => Record<string, any>;
|
|
105
|
+
render?: (node: Node, nodeUI?: any) => TemplateResult;
|
|
106
|
+
renderTitle?: (node: Node, nodeUI?: NodeUI) => TemplateResult;
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
// New field configuration system for generic form generation
|
|
@@ -130,6 +133,11 @@ export interface BaseFieldConfig {
|
|
|
130
133
|
visible?: (formData: Record<string, any>) => boolean;
|
|
131
134
|
disabled?: (formData: Record<string, any>) => boolean;
|
|
132
135
|
};
|
|
136
|
+
|
|
137
|
+
// Optional field with reveal link
|
|
138
|
+
// When set, the field is hidden by default and a link with this text is shown
|
|
139
|
+
// Clicking the link reveals the field permanently (can't be hidden again)
|
|
140
|
+
optionalLink?: string;
|
|
133
141
|
}
|
|
134
142
|
|
|
135
143
|
export interface TextFieldConfig extends BaseFieldConfig {
|
|
@@ -231,6 +239,8 @@ export interface RowLayoutConfig {
|
|
|
231
239
|
type: 'row';
|
|
232
240
|
items: LayoutItem[]; // can contain fields, groups, or other rows
|
|
233
241
|
gap?: string; // CSS gap value, defaults to '1rem'
|
|
242
|
+
label?: string; // optional label for the entire row
|
|
243
|
+
helpText?: string; // optional help text for the entire row
|
|
234
244
|
}
|
|
235
245
|
|
|
236
246
|
export interface GroupLayoutConfig {
|
|
@@ -238,9 +248,9 @@ export interface GroupLayoutConfig {
|
|
|
238
248
|
label: string;
|
|
239
249
|
items: LayoutItem[]; // can contain fields, rows, or other groups
|
|
240
250
|
collapsible?: boolean;
|
|
241
|
-
collapsed?: boolean | ((formData:
|
|
251
|
+
collapsed?: boolean | ((formData: FormData) => boolean); // initial state if collapsible - can be a function
|
|
242
252
|
helpText?: string;
|
|
243
|
-
getGroupValueCount?: (formData:
|
|
253
|
+
getGroupValueCount?: (formData: FormData) => number; // optional function to get count for bubble display
|
|
244
254
|
}
|
|
245
255
|
|
|
246
256
|
export type LayoutItem =
|
|
@@ -249,31 +259,107 @@ export type LayoutItem =
|
|
|
249
259
|
| GroupLayoutConfig
|
|
250
260
|
| string; // string is shorthand for field
|
|
251
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Action group constants - single source of truth for action categorization
|
|
264
|
+
* Use as const for compile-time type checking
|
|
265
|
+
*/
|
|
266
|
+
export const ACTION_GROUPS = {
|
|
267
|
+
send: 'send',
|
|
268
|
+
contacts: 'contacts',
|
|
269
|
+
save: 'save',
|
|
270
|
+
services: 'services',
|
|
271
|
+
broadcast: 'broadcast',
|
|
272
|
+
trigger: 'trigger'
|
|
273
|
+
} as const;
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Split group constants - single source of truth for split categorization
|
|
277
|
+
* Use as const for compile-time type checking
|
|
278
|
+
*/
|
|
279
|
+
export const SPLIT_GROUPS = {
|
|
280
|
+
wait: 'wait',
|
|
281
|
+
split: 'split'
|
|
282
|
+
} as const;
|
|
283
|
+
|
|
284
|
+
// Extract types from const objects for compile-time checking
|
|
285
|
+
export type ActionGroup = (typeof ACTION_GROUPS)[keyof typeof ACTION_GROUPS];
|
|
286
|
+
export type SplitGroup = (typeof SPLIT_GROUPS)[keyof typeof SPLIT_GROUPS];
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Metadata for group display
|
|
290
|
+
*/
|
|
291
|
+
export interface GroupMetadata {
|
|
292
|
+
color: string;
|
|
293
|
+
title: string;
|
|
294
|
+
description: string;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Action group metadata - defines display properties for each action group
|
|
299
|
+
* Order in this object determines display order in action selector (top to bottom)
|
|
300
|
+
*/
|
|
301
|
+
export const ACTION_GROUP_METADATA: Record<ActionGroup, GroupMetadata> = {
|
|
302
|
+
[ACTION_GROUPS.send]: {
|
|
303
|
+
color: '#3498db',
|
|
304
|
+
title: 'Send',
|
|
305
|
+
description: 'Actions that send messages or content to contacts'
|
|
306
|
+
},
|
|
307
|
+
[ACTION_GROUPS.contacts]: {
|
|
308
|
+
color: '#01c1af',
|
|
309
|
+
title: 'Contact',
|
|
310
|
+
description: 'Actions that update contact information'
|
|
311
|
+
},
|
|
312
|
+
[ACTION_GROUPS.save]: {
|
|
313
|
+
color: '#1a777c',
|
|
314
|
+
title: 'Save',
|
|
315
|
+
description: 'Actions that save or store data'
|
|
316
|
+
},
|
|
317
|
+
[ACTION_GROUPS.services]: {
|
|
318
|
+
color: '#f79035ff',
|
|
319
|
+
title: 'Services',
|
|
320
|
+
description: 'Call external services and APIs'
|
|
321
|
+
},
|
|
322
|
+
[ACTION_GROUPS.broadcast]: {
|
|
323
|
+
color: '#8e5ea7',
|
|
324
|
+
title: 'Other People',
|
|
325
|
+
description: 'Actions that apply to others instead of the contact'
|
|
326
|
+
},
|
|
327
|
+
[ACTION_GROUPS.trigger]: {
|
|
328
|
+
color: '#df419f',
|
|
329
|
+
title: 'Trigger',
|
|
330
|
+
description: 'Actions that trigger other behavior'
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Split group metadata - defines display properties for each split group
|
|
336
|
+
* Order in this object determines display order in split selector (top to bottom)
|
|
337
|
+
*/
|
|
338
|
+
export const SPLIT_GROUP_METADATA: Record<SplitGroup, GroupMetadata> = {
|
|
339
|
+
[SPLIT_GROUPS.wait]: {
|
|
340
|
+
color: '#4d7dad',
|
|
341
|
+
title: 'Wait',
|
|
342
|
+
description: 'Wait for user and split on their response'
|
|
343
|
+
},
|
|
344
|
+
[SPLIT_GROUPS.split]: {
|
|
345
|
+
color: '#aaaaaa',
|
|
346
|
+
title: 'Split',
|
|
347
|
+
description: 'Split the flow based on conditions'
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
|
|
252
351
|
export interface ActionConfig extends FormConfig {
|
|
253
352
|
name: string;
|
|
254
|
-
|
|
353
|
+
group: ActionGroup;
|
|
255
354
|
dialogSize?: 'small' | 'medium' | 'large' | 'xlarge';
|
|
256
355
|
evaluated?: string[];
|
|
356
|
+
hideFromActions?: boolean; // if true, don't show in action dialog (default: false - actions show in actions)
|
|
257
357
|
render?: (node: any, action: any) => TemplateResult;
|
|
258
358
|
|
|
259
359
|
form?: Record<string, FieldConfig>;
|
|
260
360
|
layout?: LayoutItem[]; // optional layout configuration - array of layout items
|
|
261
361
|
gutter?: LayoutItem[]; // fields to render in the dialog gutter (left side of buttons)
|
|
262
362
|
|
|
263
|
-
toFormData?: (action: Action) =>
|
|
264
|
-
fromFormData?: (formData:
|
|
363
|
+
toFormData?: (action: Action) => FormData;
|
|
364
|
+
fromFormData?: (formData: FormData) => Action;
|
|
265
365
|
}
|
|
266
|
-
|
|
267
|
-
export const COLORS = {
|
|
268
|
-
send: '#3498db',
|
|
269
|
-
update: '#01c1af',
|
|
270
|
-
broadcast: '#8e5ea7',
|
|
271
|
-
call: '#e68628',
|
|
272
|
-
create: '#df419f',
|
|
273
|
-
save: '#1a777c',
|
|
274
|
-
split: '#aaaaaa',
|
|
275
|
-
execute: '#666666',
|
|
276
|
-
wait: '#4d7dad',
|
|
277
|
-
add: '#309c42',
|
|
278
|
-
remove: '#e74c3c'
|
|
279
|
-
};
|
package/src/flow/utils.ts
CHANGED
|
@@ -60,17 +60,111 @@ export const renderStringList = (items: string[], icon?: string) => {
|
|
|
60
60
|
return itemElements;
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
63
|
+
export interface Scheme {
|
|
64
|
+
scheme: string;
|
|
65
|
+
name: string;
|
|
66
|
+
path: string;
|
|
67
|
+
excludeFromSplit?: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const SCHEMES: Scheme[] = [
|
|
71
|
+
{
|
|
72
|
+
scheme: 'tel',
|
|
73
|
+
name: 'SMS',
|
|
74
|
+
path: 'Phone Number'
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
scheme: 'whatsapp',
|
|
78
|
+
name: 'WhatsApp',
|
|
79
|
+
path: 'WhatsApp Number'
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
scheme: 'facebook',
|
|
83
|
+
name: 'Facebook',
|
|
84
|
+
path: 'Facebook ID'
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
scheme: 'instagram',
|
|
88
|
+
name: 'Instagram',
|
|
89
|
+
path: 'Instagram ID'
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
scheme: 'twitterid',
|
|
93
|
+
name: 'Twitter',
|
|
94
|
+
path: 'Twitter ID',
|
|
95
|
+
excludeFromSplit: true
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
scheme: 'telegram',
|
|
99
|
+
name: 'Telegram',
|
|
100
|
+
path: 'Telegram ID'
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
scheme: 'viber',
|
|
104
|
+
name: 'Viber',
|
|
105
|
+
path: 'Viber ID'
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
scheme: 'line',
|
|
109
|
+
name: 'Line',
|
|
110
|
+
path: 'Line ID'
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
scheme: 'wechat',
|
|
114
|
+
name: 'WeChat',
|
|
115
|
+
path: 'WeChat ID'
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
scheme: 'fcm',
|
|
119
|
+
name: 'Firebase',
|
|
120
|
+
path: 'Firebase ID'
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
scheme: 'jiochat',
|
|
124
|
+
name: 'JioChat',
|
|
125
|
+
path: 'JioChat ID'
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
scheme: 'freshchat',
|
|
129
|
+
name: 'Freshchat',
|
|
130
|
+
path: 'Freshchat ID'
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
scheme: 'mailto',
|
|
134
|
+
name: 'Email',
|
|
135
|
+
path: 'Email Address',
|
|
136
|
+
excludeFromSplit: true
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
scheme: 'twitter',
|
|
140
|
+
name: 'Twitter',
|
|
141
|
+
path: 'Twitter Handle',
|
|
142
|
+
excludeFromSplit: true
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
scheme: 'vk',
|
|
146
|
+
name: 'VK',
|
|
147
|
+
path: 'VK ID'
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
scheme: 'discord',
|
|
151
|
+
name: 'Discord',
|
|
152
|
+
path: 'Discord ID'
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
scheme: 'webchat',
|
|
156
|
+
name: 'Webchat',
|
|
157
|
+
path: 'Webchat ID',
|
|
158
|
+
excludeFromSplit: true
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
scheme: 'rocketchat',
|
|
162
|
+
name: 'RocketChat',
|
|
163
|
+
path: 'RocketChat ID'
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
scheme: 'ext',
|
|
167
|
+
name: 'External',
|
|
168
|
+
path: 'External ID'
|
|
169
|
+
}
|
|
170
|
+
];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { html, TemplateResult } from 'lit';
|
|
2
|
+
import { spread } from '@open-wc/lit-helpers';
|
|
2
3
|
import {
|
|
3
4
|
FieldConfig,
|
|
4
5
|
TextFieldConfig,
|
|
@@ -245,10 +246,7 @@ export class FieldRenderer {
|
|
|
245
246
|
value="${option}"
|
|
246
247
|
></temba-option>`;
|
|
247
248
|
} else {
|
|
248
|
-
return html`<temba-option
|
|
249
|
-
name="${option.label || option.name}"
|
|
250
|
-
value="${option.value}"
|
|
251
|
-
></temba-option>`;
|
|
249
|
+
return html`<temba-option ${spread(option)}></temba-option>`;
|
|
252
250
|
}
|
|
253
251
|
})}
|
|
254
252
|
</temba-select>`;
|
package/src/interfaces.ts
CHANGED
|
@@ -278,6 +278,8 @@ export enum CustomEventType {
|
|
|
278
278
|
OrderChanged = 'temba-order-changed',
|
|
279
279
|
DragStart = 'temba-drag-start',
|
|
280
280
|
DragStop = 'temba-drag-stop',
|
|
281
|
+
DragExternal = 'temba-drag-external',
|
|
282
|
+
DragInternal = 'temba-drag-internal',
|
|
281
283
|
Resized = 'temba-resized',
|
|
282
284
|
DetailsChanged = 'temba-details-changed',
|
|
283
285
|
Error = 'temba-error',
|
|
@@ -288,6 +290,7 @@ export enum CustomEventType {
|
|
|
288
290
|
DateRangeChanged = 'temba-date-range-changed',
|
|
289
291
|
NodeDeleted = 'temba-node-deleted',
|
|
290
292
|
ActionEditRequested = 'temba-action-edit-requested',
|
|
293
|
+
AddActionRequested = 'temba-add-action-requested',
|
|
291
294
|
ActionSaved = 'temba-action-saved',
|
|
292
295
|
ActionEditCanceled = 'temba-action-edit-canceled',
|
|
293
296
|
NodeEditRequested = 'temba-node-edit-requested',
|
package/src/list/SortableList.ts
CHANGED
|
@@ -9,6 +9,10 @@ import { RapidElement } from '../RapidElement';
|
|
|
9
9
|
|
|
10
10
|
// how far we have to drag before it starts
|
|
11
11
|
const DRAG_THRESHOLD = 2;
|
|
12
|
+
|
|
13
|
+
// padding around container for external drag detection
|
|
14
|
+
const EXTERNAL_DRAG_PADDING = 50;
|
|
15
|
+
|
|
12
16
|
export class SortableList extends RapidElement {
|
|
13
17
|
originalDownDisplay: string;
|
|
14
18
|
static get styles() {
|
|
@@ -65,6 +69,9 @@ export class SortableList extends RapidElement {
|
|
|
65
69
|
@property({ type: String })
|
|
66
70
|
gap: string = '0em';
|
|
67
71
|
|
|
72
|
+
@property({ type: Boolean })
|
|
73
|
+
externalDrag: boolean = false;
|
|
74
|
+
|
|
68
75
|
/**
|
|
69
76
|
* Optional callback to allow parent components to customize the ghost node.
|
|
70
77
|
* Called after the ghost node is cloned but before it is appended to the DOM.
|
|
@@ -86,6 +93,7 @@ export class SortableList extends RapidElement {
|
|
|
86
93
|
dropPlaceholder: HTMLDivElement = null;
|
|
87
94
|
pendingDropIndex = -1;
|
|
88
95
|
pendingTargetElement: HTMLElement = null;
|
|
96
|
+
isExternalDrag = false;
|
|
89
97
|
|
|
90
98
|
private clickBlocker: ((e: MouseEvent) => void) | null = null;
|
|
91
99
|
|
|
@@ -108,6 +116,20 @@ export class SortableList extends RapidElement {
|
|
|
108
116
|
return eles;
|
|
109
117
|
}
|
|
110
118
|
|
|
119
|
+
private isMouseOverContainer(mouseX: number, mouseY: number): boolean {
|
|
120
|
+
const container = this.shadowRoot.querySelector('.container');
|
|
121
|
+
if (!container) return false;
|
|
122
|
+
|
|
123
|
+
const rect = container.getBoundingClientRect();
|
|
124
|
+
// add some padding to make it easier to stay within the container
|
|
125
|
+
return (
|
|
126
|
+
mouseX >= rect.left - EXTERNAL_DRAG_PADDING &&
|
|
127
|
+
mouseX <= rect.right + EXTERNAL_DRAG_PADDING &&
|
|
128
|
+
mouseY >= rect.top - EXTERNAL_DRAG_PADDING &&
|
|
129
|
+
mouseY <= rect.bottom + EXTERNAL_DRAG_PADDING
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
111
133
|
private cloneElementWithState(element: HTMLElement): HTMLElement {
|
|
112
134
|
// First create a basic clone
|
|
113
135
|
const clone = element.cloneNode(true) as HTMLElement;
|
|
@@ -305,6 +327,8 @@ export class SortableList extends RapidElement {
|
|
|
305
327
|
this.dropPlaceholder.style.minHeight = rect.height + 'px';
|
|
306
328
|
this.dropPlaceholder.style.borderRadius = 'var(--curvature)';
|
|
307
329
|
this.dropPlaceholder.style.flexShrink = '0';
|
|
330
|
+
this.dropPlaceholder.style.background = '#f3f4f6';
|
|
331
|
+
this.dropPlaceholder.style.border = '2px dashed #d1d5db';
|
|
308
332
|
}
|
|
309
333
|
|
|
310
334
|
// Insert the placeholder in the correct position in the DOM
|
|
@@ -335,10 +359,8 @@ export class SortableList extends RapidElement {
|
|
|
335
359
|
this.dropPlaceholder.style.minHeight = rect.height + 'px';
|
|
336
360
|
this.dropPlaceholder.style.borderRadius = 'var(--curvature)';
|
|
337
361
|
this.dropPlaceholder.style.flexShrink = '0';
|
|
338
|
-
this.dropPlaceholder.style.background =
|
|
339
|
-
|
|
340
|
-
this.dropPlaceholder.style.border =
|
|
341
|
-
'2px dashed rgba(var(--color-primary-rgb), 0.3)';
|
|
362
|
+
this.dropPlaceholder.style.background = '#f3f4f6';
|
|
363
|
+
this.dropPlaceholder.style.border = '2px dashed #d1d5db';
|
|
342
364
|
|
|
343
365
|
// Insert the placeholder right after the hidden original element
|
|
344
366
|
this.downEle.insertAdjacentElement('afterend', this.dropPlaceholder);
|
|
@@ -440,41 +462,86 @@ export class SortableList extends RapidElement {
|
|
|
440
462
|
this.ghostElement.style.left = event.clientX - this.xOffset + 'px';
|
|
441
463
|
this.ghostElement.style.top = event.clientY - this.yOffset + 'px';
|
|
442
464
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
465
|
+
// check if the drag is over the container (only if external dragging is allowed)
|
|
466
|
+
const isOverContainer = this.externalDrag
|
|
467
|
+
? this.isMouseOverContainer(event.clientX, event.clientY)
|
|
468
|
+
: true; // always consider "over container" if external drag is disabled
|
|
447
469
|
|
|
448
|
-
|
|
449
|
-
|
|
470
|
+
// detect transition between internal and external drag (only if allowed)
|
|
471
|
+
if (this.externalDrag && !isOverContainer && !this.isExternalDrag) {
|
|
472
|
+
// transitioning to external drag
|
|
473
|
+
this.isExternalDrag = true;
|
|
474
|
+
this.hideDropPlaceholder();
|
|
450
475
|
|
|
451
|
-
//
|
|
452
|
-
|
|
476
|
+
// hide the ghost element when dragging externally
|
|
477
|
+
if (this.ghostElement) {
|
|
478
|
+
this.ghostElement.style.display = 'none';
|
|
479
|
+
}
|
|
453
480
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
481
|
+
this.fireCustomEvent(CustomEventType.DragExternal, {
|
|
482
|
+
id: this.downEle.id,
|
|
483
|
+
mouseX: event.clientX,
|
|
484
|
+
mouseY: event.clientY
|
|
485
|
+
});
|
|
486
|
+
} else if (this.externalDrag && isOverContainer && this.isExternalDrag) {
|
|
487
|
+
// transitioning back to internal drag
|
|
488
|
+
this.isExternalDrag = false;
|
|
489
|
+
|
|
490
|
+
// show the ghost element again when dragging internally
|
|
491
|
+
if (this.ghostElement) {
|
|
492
|
+
this.ghostElement.style.display = 'block';
|
|
464
493
|
}
|
|
465
494
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
495
|
+
this.fireCustomEvent(CustomEventType.DragInternal, {
|
|
496
|
+
id: this.downEle.id
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// only show drop placeholder and calculate drop position if internal drag
|
|
501
|
+
if (!this.isExternalDrag) {
|
|
502
|
+
const targetInfo = this.getDropTargetInfo(event.clientX, event.clientY);
|
|
503
|
+
if (targetInfo) {
|
|
504
|
+
const { element: targetElement, insertAfter } = targetInfo;
|
|
505
|
+
const targetIdx = this.getRowIndex(targetElement.id);
|
|
506
|
+
|
|
507
|
+
// Use the original drag index we captured before moving the element
|
|
508
|
+
const originalDragIdx = this.originalDragIndex;
|
|
509
|
+
|
|
510
|
+
// Calculate where the dragged element will end up in the final array
|
|
511
|
+
// targetIdx is the position of target element in current DOM (missing dragged element)
|
|
512
|
+
|
|
513
|
+
let dropIdx;
|
|
514
|
+
if (targetIdx < originalDragIdx) {
|
|
515
|
+
// Target is before the original drag position - moving backward
|
|
516
|
+
dropIdx = insertAfter ? targetIdx + 1 : targetIdx;
|
|
517
|
+
} else {
|
|
518
|
+
// Target was originally after the drag position - moving forward
|
|
519
|
+
// When moving the dragged element forward (i.e., to a higher index), the targetIdx is based on the current DOM,
|
|
520
|
+
// which no longer includes the dragged element. This means all elements after the original position have shifted left by one,
|
|
521
|
+
// so we need to subtract 1 from targetIdx to get the correct insertion index. If inserting after the target, we use targetIdx as is.
|
|
522
|
+
dropIdx = insertAfter ? targetIdx : targetIdx - 1;
|
|
523
|
+
}
|
|
470
524
|
|
|
471
|
-
|
|
472
|
-
|
|
525
|
+
// Store pending drop info but don't fire event yet
|
|
526
|
+
this.dropTargetId = targetElement.id;
|
|
527
|
+
this.pendingDropIndex = dropIdx;
|
|
528
|
+
this.pendingTargetElement = targetElement;
|
|
529
|
+
|
|
530
|
+
// Show drop placeholder
|
|
531
|
+
this.showDropPlaceholder(targetElement, insertAfter);
|
|
532
|
+
} else {
|
|
533
|
+
this.hideDropPlaceholder();
|
|
534
|
+
this.dropTargetId = null;
|
|
535
|
+
this.pendingDropIndex = -1;
|
|
536
|
+
this.pendingTargetElement = null;
|
|
537
|
+
}
|
|
473
538
|
} else {
|
|
474
|
-
|
|
475
|
-
this.
|
|
476
|
-
|
|
477
|
-
|
|
539
|
+
// external drag - continue firing external drag events with updated position
|
|
540
|
+
this.fireCustomEvent(CustomEventType.DragExternal, {
|
|
541
|
+
id: this.downEle.id,
|
|
542
|
+
mouseX: event.clientX,
|
|
543
|
+
mouseY: event.clientY
|
|
544
|
+
});
|
|
478
545
|
}
|
|
479
546
|
}
|
|
480
547
|
}
|
|
@@ -498,7 +565,11 @@ export class SortableList extends RapidElement {
|
|
|
498
565
|
this.hideDropPlaceholder();
|
|
499
566
|
|
|
500
567
|
// fire the order changed event only when dropped if we have a valid drop position
|
|
501
|
-
if (
|
|
568
|
+
if (
|
|
569
|
+
!this.isExternalDrag &&
|
|
570
|
+
this.pendingDropIndex >= 0 &&
|
|
571
|
+
this.pendingTargetElement
|
|
572
|
+
) {
|
|
502
573
|
// Use the original drag index we captured before hiding the element
|
|
503
574
|
const originalDragIdx = this.originalDragIndex;
|
|
504
575
|
|
|
@@ -515,7 +586,10 @@ export class SortableList extends RapidElement {
|
|
|
515
586
|
}
|
|
516
587
|
|
|
517
588
|
this.fireCustomEvent(CustomEventType.DragStop, {
|
|
518
|
-
id: this.draggingId
|
|
589
|
+
id: this.draggingId,
|
|
590
|
+
isExternal: this.isExternalDrag,
|
|
591
|
+
mouseX: evt.clientX,
|
|
592
|
+
mouseY: evt.clientY
|
|
519
593
|
});
|
|
520
594
|
|
|
521
595
|
this.draggingId = null;
|
|
@@ -525,6 +599,7 @@ export class SortableList extends RapidElement {
|
|
|
525
599
|
this.originalDragIndex = -1;
|
|
526
600
|
this.pendingDropIndex = -1;
|
|
527
601
|
this.pendingTargetElement = null;
|
|
602
|
+
this.isExternalDrag = false;
|
|
528
603
|
|
|
529
604
|
// Clear the ghost reference since we removed it
|
|
530
605
|
this.ghostElement = null;
|
package/src/live/ContactChat.ts
CHANGED
|
@@ -808,16 +808,19 @@ export class ContactChat extends ContactStoreElement {
|
|
|
808
808
|
};
|
|
809
809
|
} else if (event._user) {
|
|
810
810
|
return event._user;
|
|
811
|
-
} else if (event.created_by) {
|
|
812
|
-
return {
|
|
813
|
-
email: event.created_by.email,
|
|
814
|
-
name: `${event.created_by.first_name} ${event.created_by.last_name}`.trim(),
|
|
815
|
-
avatar: event.created_by.avatar
|
|
816
|
-
};
|
|
817
811
|
}
|
|
818
812
|
return null;
|
|
819
813
|
}
|
|
820
814
|
|
|
815
|
+
private isMessageError(status: string | { status: string }): boolean {
|
|
816
|
+
if (typeof status === 'string') {
|
|
817
|
+
return status === 'E' || status === 'F';
|
|
818
|
+
} else if (status && typeof status === 'object' && 'status' in status) {
|
|
819
|
+
return status.status === 'errored' || status.status === 'failed';
|
|
820
|
+
}
|
|
821
|
+
return false;
|
|
822
|
+
}
|
|
823
|
+
|
|
821
824
|
private createMessages(page: ContactHistoryPage): ChatEvent[] {
|
|
822
825
|
if (page.events) {
|
|
823
826
|
let messages = [];
|
|
@@ -852,13 +855,9 @@ export class ContactChat extends ContactStoreElement {
|
|
|
852
855
|
} else if (
|
|
853
856
|
event.type === 'msg_created' ||
|
|
854
857
|
event.type === 'msg_received' ||
|
|
855
|
-
event.type === 'ivr_created'
|
|
856
|
-
event.type === 'broadcast_created'
|
|
858
|
+
event.type === 'ivr_created'
|
|
857
859
|
) {
|
|
858
860
|
const msgEvent = event as MsgEvent;
|
|
859
|
-
const status = msgEvent.status || msgEvent._status;
|
|
860
|
-
const failedReason =
|
|
861
|
-
msgEvent.failed_reason_display || msgEvent._failed_reason;
|
|
862
861
|
|
|
863
862
|
messages.push({
|
|
864
863
|
id: event.uuid,
|
|
@@ -867,7 +866,7 @@ export class ContactChat extends ContactStoreElement {
|
|
|
867
866
|
date: new Date(msgEvent.created_on),
|
|
868
867
|
attachments: msgEvent.msg.attachments,
|
|
869
868
|
text: msgEvent.msg.text,
|
|
870
|
-
sendError:
|
|
869
|
+
sendError: this.isMessageError(msgEvent._status),
|
|
871
870
|
popup: html`<div
|
|
872
871
|
style="display: flex; flex-direction: row; align-items:center; justify-content: space-between;font-size:0.9em;line-height:1em;min-width:10em"
|
|
873
872
|
>
|
|
@@ -882,20 +881,18 @@ export class ContactChat extends ContactStoreElement {
|
|
|
882
881
|
${msgEvent.optin.name}
|
|
883
882
|
</div>`
|
|
884
883
|
: null}
|
|
885
|
-
${
|
|
884
|
+
${msgEvent._failed_reason
|
|
886
885
|
? html`
|
|
887
886
|
<div
|
|
888
887
|
style="margin-top:0.2em;margin-right: 0.5em;min-width:10em;max-width:15em;color:var(--color-error);font-size:0.9em"
|
|
889
888
|
>
|
|
890
|
-
${
|
|
889
|
+
${msgEvent._failed_reason}
|
|
891
890
|
</div>
|
|
892
891
|
`
|
|
893
892
|
: null}
|
|
894
893
|
</div>
|
|
895
|
-
${msgEvent.
|
|
896
|
-
? html`<a
|
|
897
|
-
style="margin-left:0.5em"
|
|
898
|
-
href="${msgEvent.logs_url || msgEvent._logs_url}"
|
|
894
|
+
${msgEvent._logs_url
|
|
895
|
+
? html`<a style="margin-left:0.5em" href="${msgEvent._logs_url}"
|
|
899
896
|
><temba-icon name="log"></temba-icon
|
|
900
897
|
></a>`
|
|
901
898
|
: null}
|