@nyaruka/temba-components 0.139.0 → 0.141.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/cla.yml +1 -1
- package/.github/workflows/copilot-setup-steps.yml +6 -1
- package/.lintstagedrc.js +10 -0
- package/CHANGELOG.md +32 -0
- package/demo/data/flows/sample-flow.json +24 -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/temba-components.js +702 -338
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/display/Chat.js +10 -7
- package/out-tsc/src/display/Chat.js.map +1 -1
- package/out-tsc/src/display/Dropdown.js +3 -1
- package/out-tsc/src/display/Dropdown.js.map +1 -1
- package/out-tsc/src/display/FloatingTab.js +4 -4
- package/out-tsc/src/display/FloatingTab.js.map +1 -1
- package/out-tsc/src/display/Thumbnail.js +163 -5
- package/out-tsc/src/display/Thumbnail.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +65 -23
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +369 -49
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +118 -10
- package/out-tsc/src/flow/NodeEditor.js.map +1 -1
- package/out-tsc/src/flow/Plumber.js +61 -14
- package/out-tsc/src/flow/Plumber.js.map +1 -1
- package/out-tsc/src/flow/StickyNote.js +13 -4
- package/out-tsc/src/flow/StickyNote.js.map +1 -1
- package/out-tsc/src/flow/actions/add_contact_groups.js +4 -1
- package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
- package/out-tsc/src/flow/actions/add_input_labels.js +4 -1
- package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
- package/out-tsc/src/flow/actions/audio-player.js +112 -0
- package/out-tsc/src/flow/actions/audio-player.js.map +1 -0
- package/out-tsc/src/flow/actions/enter_flow.js +43 -0
- package/out-tsc/src/flow/actions/enter_flow.js.map +1 -0
- package/out-tsc/src/flow/actions/play_audio.js +57 -4
- package/out-tsc/src/flow/actions/play_audio.js.map +1 -1
- package/out-tsc/src/flow/actions/remove_contact_groups.js +6 -1
- package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -1
- package/out-tsc/src/flow/actions/say_msg.js +86 -3
- package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
- package/out-tsc/src/flow/actions/send_broadcast.js +6 -2
- package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_channel.js +13 -0
- package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_status.js +7 -5
- package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
- package/out-tsc/src/flow/actions/start_session.js +10 -3
- package/out-tsc/src/flow/actions/start_session.js.map +1 -1
- package/out-tsc/src/flow/config.js +11 -3
- package/out-tsc/src/flow/config.js.map +1 -1
- package/out-tsc/src/flow/nodes/shared-rules.js +1 -1
- package/out-tsc/src/flow/nodes/shared-rules.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_contact_field.js +18 -5
- package/out-tsc/src/flow/nodes/split_by_contact_field.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_expression.js +1 -1
- package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +0 -1
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_random.js +0 -1
- package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_run_result.js +10 -4
- package/out-tsc/src/flow/nodes/split_by_run_result.js.map +1 -1
- package/out-tsc/src/flow/nodes/terminal.js +7 -0
- package/out-tsc/src/flow/nodes/terminal.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_audio.js +77 -0
- package/out-tsc/src/flow/nodes/wait_for_audio.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_dial.js +151 -0
- package/out-tsc/src/flow/nodes/wait_for_dial.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_digits.js +61 -1
- package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_menu.js +173 -2
- package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js +1 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
- package/out-tsc/src/flow/operators.js +21 -5
- package/out-tsc/src/flow/operators.js.map +1 -1
- package/out-tsc/src/flow/types.js.map +1 -1
- package/out-tsc/src/flow/utils.js +79 -3
- package/out-tsc/src/flow/utils.js.map +1 -1
- package/out-tsc/src/form/ArrayEditor.js +4 -2
- package/out-tsc/src/form/ArrayEditor.js.map +1 -1
- package/out-tsc/src/form/FieldRenderer.js +56 -0
- package/out-tsc/src/form/FieldRenderer.js.map +1 -1
- package/out-tsc/src/interfaces.js +1 -0
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/src/layout/Dialog.js +51 -7
- package/out-tsc/src/layout/Dialog.js.map +1 -1
- package/out-tsc/src/layout/Modax.js +20 -2
- package/out-tsc/src/layout/Modax.js.map +1 -1
- package/out-tsc/src/list/ContentMenu.js +14 -1
- package/out-tsc/src/list/ContentMenu.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/simulator/Simulator.js +21 -4
- package/out-tsc/src/simulator/Simulator.js.map +1 -1
- package/out-tsc/src/store/AppState.js +102 -3
- package/out-tsc/src/store/AppState.js.map +1 -1
- package/out-tsc/test/actions/add_contact_groups.test.js +35 -0
- package/out-tsc/test/actions/add_contact_groups.test.js.map +1 -1
- package/out-tsc/test/actions/add_input_labels.test.js +53 -0
- package/out-tsc/test/actions/add_input_labels.test.js.map +1 -0
- package/out-tsc/test/actions/enter_flow.test.js +71 -0
- package/out-tsc/test/actions/enter_flow.test.js.map +1 -0
- package/out-tsc/test/actions/play_audio.test.js +118 -0
- package/out-tsc/test/actions/play_audio.test.js.map +1 -0
- package/out-tsc/test/actions/remove_contact_groups.test.js +24 -0
- package/out-tsc/test/actions/remove_contact_groups.test.js.map +1 -1
- package/out-tsc/test/actions/say_msg.test.js +158 -0
- package/out-tsc/test/actions/say_msg.test.js.map +1 -0
- package/out-tsc/test/actions/send_broadcast.test.js +41 -0
- package/out-tsc/test/actions/send_broadcast.test.js.map +1 -1
- package/out-tsc/test/actions/set_contact_channel.test.js +67 -0
- package/out-tsc/test/actions/set_contact_channel.test.js.map +1 -0
- package/out-tsc/test/actions/set_contact_field.test.js +52 -0
- package/out-tsc/test/actions/set_contact_field.test.js.map +1 -0
- package/out-tsc/test/actions/set_contact_language.test.js +39 -0
- package/out-tsc/test/actions/set_contact_language.test.js.map +1 -0
- package/out-tsc/test/actions/set_contact_name.test.js +28 -0
- package/out-tsc/test/actions/set_contact_name.test.js.map +1 -0
- package/out-tsc/test/actions/set_contact_status.test.js +44 -0
- package/out-tsc/test/actions/set_contact_status.test.js.map +1 -0
- package/out-tsc/test/actions/set_run_result.test.js +47 -0
- package/out-tsc/test/actions/set_run_result.test.js.map +1 -0
- package/out-tsc/test/actions/start_session.test.js +76 -0
- package/out-tsc/test/actions/start_session.test.js.map +1 -1
- package/out-tsc/test/nodes/split_by_contact_field.test.js +50 -0
- package/out-tsc/test/nodes/split_by_contact_field.test.js.map +1 -1
- package/out-tsc/test/nodes/split_by_run_result.test.js +82 -0
- package/out-tsc/test/nodes/split_by_run_result.test.js.map +1 -1
- package/out-tsc/test/nodes/split_by_ticket.test.js +139 -0
- package/out-tsc/test/nodes/split_by_ticket.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_webhook.test.js +111 -0
- package/out-tsc/test/nodes/split_by_webhook.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_audio.test.js +156 -0
- package/out-tsc/test/nodes/wait_for_audio.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_dial.test.js +336 -0
- package/out-tsc/test/nodes/wait_for_dial.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_digits.test.js +198 -84
- package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -1
- package/out-tsc/test/nodes/wait_for_menu.test.js +340 -0
- package/out-tsc/test/nodes/wait_for_menu.test.js.map +1 -0
- package/out-tsc/test/temba-flow-collision.test.js +261 -6
- package/out-tsc/test/temba-flow-collision.test.js.map +1 -1
- package/out-tsc/test/temba-flow-editor.test.js +187 -0
- package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
- package/out-tsc/test/temba-flow-plumber.test.js +19 -0
- package/out-tsc/test/temba-flow-plumber.test.js.map +1 -1
- package/out-tsc/test/temba-node-type-selector.test.js +6 -6
- package/out-tsc/test/temba-node-type-selector.test.js.map +1 -1
- package/out-tsc/test/temba-select.test.js +4 -1
- package/out-tsc/test/temba-select.test.js.map +1 -1
- package/out-tsc/test/utils.test.js +4 -2
- package/out-tsc/test/utils.test.js.map +1 -1
- package/package.json +3 -9
- 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/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/add_input_labels/editor/multiple-labels.png +0 -0
- package/screenshots/truth/actions/add_input_labels/editor/single-label.png +0 -0
- package/screenshots/truth/actions/add_input_labels/render/multiple-labels.png +0 -0
- package/screenshots/truth/actions/add_input_labels/render/single-label.png +0 -0
- package/screenshots/truth/actions/enter_flow/editor/basic-flow.png +0 -0
- package/screenshots/truth/actions/enter_flow/editor/long-flow-name.png +0 -0
- package/screenshots/truth/actions/enter_flow/render/basic-flow.png +0 -0
- package/screenshots/truth/actions/enter_flow/render/long-flow-name.png +0 -0
- package/screenshots/truth/actions/play_audio/editor/expression-url.png +0 -0
- package/screenshots/truth/actions/play_audio/editor/static-url.png +0 -0
- package/screenshots/truth/actions/play_audio/render/expression-url.png +0 -0
- package/screenshots/truth/actions/play_audio/render/static-url.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/say_msg/editor/multiline-text.png +0 -0
- package/screenshots/truth/actions/say_msg/editor/simple-text.png +0 -0
- package/screenshots/truth/actions/say_msg/editor/text-with-audio-url.png +0 -0
- package/screenshots/truth/actions/say_msg/render/multiline-text.png +0 -0
- package/screenshots/truth/actions/say_msg/render/simple-text.png +0 -0
- package/screenshots/truth/actions/say_msg/render/text-with-audio-url.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_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/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/set_contact_channel/editor/sms-channel.png +0 -0
- package/screenshots/truth/actions/set_contact_channel/editor/whatsapp-channel.png +0 -0
- package/screenshots/truth/actions/set_contact_channel/render/sms-channel.png +0 -0
- package/screenshots/truth/actions/set_contact_channel/render/whatsapp-channel.png +0 -0
- package/screenshots/truth/actions/set_contact_field/editor/clear-value.png +0 -0
- package/screenshots/truth/actions/set_contact_field/editor/set-value.png +0 -0
- package/screenshots/truth/actions/set_contact_field/render/clear-value.png +0 -0
- package/screenshots/truth/actions/set_contact_field/render/set-value.png +0 -0
- package/screenshots/truth/actions/set_contact_language/editor/english.png +0 -0
- package/screenshots/truth/actions/set_contact_language/editor/french.png +0 -0
- package/screenshots/truth/actions/set_contact_language/render/english.png +0 -0
- package/screenshots/truth/actions/set_contact_language/render/french.png +0 -0
- package/screenshots/truth/actions/set_contact_name/editor/expression-name.png +0 -0
- package/screenshots/truth/actions/set_contact_name/editor/static-name.png +0 -0
- package/screenshots/truth/actions/set_contact_name/render/expression-name.png +0 -0
- package/screenshots/truth/actions/set_contact_name/render/static-name.png +0 -0
- package/screenshots/truth/actions/set_contact_status/editor/active.png +0 -0
- package/screenshots/truth/actions/set_contact_status/editor/archived.png +0 -0
- package/screenshots/truth/actions/set_contact_status/editor/blocked.png +0 -0
- package/screenshots/truth/actions/set_contact_status/render/active.png +0 -0
- package/screenshots/truth/actions/set_contact_status/render/archived.png +0 -0
- package/screenshots/truth/actions/set_contact_status/render/blocked.png +0 -0
- package/screenshots/truth/actions/set_run_result/editor/expression-value.png +0 -0
- package/screenshots/truth/actions/set_run_result/editor/with-category.png +0 -0
- package/screenshots/truth/actions/set_run_result/render/expression-value.png +0 -0
- package/screenshots/truth/actions/set_run_result/render/with-category.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/editor/router.png +0 -0
- package/screenshots/truth/editor/wait.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_audio/editor/basic-audio-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_audio/render/basic-audio-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_dial/editor/basic-dial.png +0 -0
- package/screenshots/truth/nodes/wait_for_dial/editor/dial-with-limits.png +0 -0
- package/screenshots/truth/nodes/wait_for_dial/render/basic-dial.png +0 -0
- package/screenshots/truth/nodes/wait_for_dial/render/dial-with-limits.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/digits-with-rules.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/digits-with-rules.png +0 -0
- package/screenshots/truth/nodes/wait_for_menu/editor/menu-with-digits.png +0 -0
- package/screenshots/truth/nodes/wait_for_menu/render/menu-with-digits.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/display/Chat.ts +13 -7
- package/src/display/Dropdown.ts +3 -1
- package/src/display/FloatingTab.ts +4 -4
- package/src/display/Thumbnail.ts +162 -2
- package/src/flow/CanvasNode.ts +70 -24
- package/src/flow/Editor.ts +440 -99
- package/src/flow/NodeEditor.ts +137 -9
- package/src/flow/Plumber.ts +89 -14
- package/src/flow/StickyNote.ts +14 -4
- package/src/flow/actions/add_contact_groups.ts +4 -1
- package/src/flow/actions/add_input_labels.ts +4 -1
- package/src/flow/actions/audio-player.ts +127 -0
- package/src/flow/actions/enter_flow.ts +44 -0
- package/src/flow/actions/play_audio.ts +64 -5
- package/src/flow/actions/remove_contact_groups.ts +6 -1
- package/src/flow/actions/say_msg.ts +94 -4
- package/src/flow/actions/send_broadcast.ts +6 -2
- package/src/flow/actions/set_contact_channel.ts +13 -1
- package/src/flow/actions/set_contact_status.ts +7 -5
- package/src/flow/actions/start_session.ts +10 -3
- package/src/flow/config.ts +11 -3
- package/src/flow/nodes/shared-rules.ts +1 -1
- package/src/flow/nodes/split_by_contact_field.ts +16 -5
- package/src/flow/nodes/split_by_expression.ts +1 -1
- package/src/flow/nodes/split_by_llm_categorize.ts +0 -1
- package/src/flow/nodes/split_by_random.ts +0 -1
- package/src/flow/nodes/split_by_run_result.ts +10 -4
- package/src/flow/nodes/terminal.ts +9 -0
- package/src/flow/nodes/wait_for_audio.ts +88 -0
- package/src/flow/nodes/wait_for_dial.ts +176 -0
- package/src/flow/nodes/wait_for_digits.ts +87 -2
- package/src/flow/nodes/wait_for_menu.ts +209 -3
- package/src/flow/nodes/wait_for_response.ts +1 -1
- package/src/flow/operators.ts +23 -5
- package/src/flow/types.ts +23 -1
- package/src/flow/utils.ts +82 -3
- package/src/form/ArrayEditor.ts +4 -2
- package/src/form/FieldRenderer.ts +71 -1
- package/src/interfaces.ts +2 -1
- package/src/layout/Dialog.ts +52 -7
- package/src/layout/Modax.ts +19 -2
- package/src/list/ContentMenu.ts +15 -1
- 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/simulator/Simulator.ts +25 -4
- package/src/store/AppState.ts +120 -1
- package/src/store/flow-definition.d.ts +2 -0
- package/test/actions/add_contact_groups.test.ts +38 -0
- package/test/actions/add_input_labels.test.ts +67 -0
- package/test/actions/enter_flow.test.ts +88 -0
- package/test/actions/play_audio.test.ts +155 -0
- package/test/actions/remove_contact_groups.test.ts +29 -0
- package/test/actions/say_msg.test.ts +196 -0
- package/test/actions/send_broadcast.test.ts +44 -0
- package/test/actions/set_contact_channel.test.ts +88 -0
- package/test/actions/set_contact_field.test.ts +68 -0
- package/test/actions/set_contact_language.test.ts +55 -0
- package/test/actions/set_contact_name.test.ts +39 -0
- package/test/actions/set_contact_status.test.ts +64 -0
- package/test/actions/set_run_result.test.ts +61 -0
- package/test/actions/start_session.test.ts +82 -0
- package/test/nodes/split_by_contact_field.test.ts +59 -0
- package/test/nodes/split_by_run_result.test.ts +100 -0
- package/test/nodes/split_by_ticket.test.ts +157 -0
- package/test/nodes/split_by_webhook.test.ts +131 -0
- package/test/nodes/wait_for_audio.test.ts +182 -0
- package/test/nodes/wait_for_dial.test.ts +382 -0
- package/test/nodes/wait_for_digits.test.ts +233 -109
- package/test/nodes/wait_for_menu.test.ts +383 -0
- package/test/temba-flow-collision.test.ts +286 -6
- package/test/temba-flow-editor.test.ts +240 -0
- package/test/temba-flow-plumber.test.ts +62 -0
- package/test/temba-node-type-selector.test.ts +6 -6
- package/test/temba-select.test.ts +6 -1
- package/test/utils.test.ts +4 -2
- 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/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/src/flow/utils.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { html } from 'lit-html';
|
|
2
2
|
import { NamedObject, FlowPosition } from '../store/flow-definition';
|
|
3
|
+
import { FlowIssue } from '../store/AppState';
|
|
4
|
+
|
|
5
|
+
export function formatIssueMessage(issue: FlowIssue): string {
|
|
6
|
+
if (issue.dependency) {
|
|
7
|
+
const name = issue.dependency.name || issue.dependency.key;
|
|
8
|
+
return `Cannot find a ${issue.dependency.type} for ${name}`;
|
|
9
|
+
}
|
|
10
|
+
return issue.description;
|
|
11
|
+
}
|
|
3
12
|
|
|
4
13
|
const GRID_SIZE = 20;
|
|
5
14
|
|
|
@@ -346,6 +355,25 @@ export const calculateReflowPositions = (
|
|
|
346
355
|
currentBounds.set(b.uuid, { ...b });
|
|
347
356
|
}
|
|
348
357
|
|
|
358
|
+
// A sacred node yields to an existing node at the top of the canvas when
|
|
359
|
+
// the sacred wasn't dropped above it. The existing node keeps its top
|
|
360
|
+
// position and the sacred node moves below instead.
|
|
361
|
+
for (const sacredUuid of [...sacredSet]) {
|
|
362
|
+
const sacred = currentBounds.get(sacredUuid);
|
|
363
|
+
if (!sacred) continue;
|
|
364
|
+
|
|
365
|
+
for (const [uuid, bounds] of currentBounds) {
|
|
366
|
+
if (uuid === sacredUuid || sacredSet.has(uuid)) continue;
|
|
367
|
+
if (!nodesOverlap(sacred, bounds)) continue;
|
|
368
|
+
|
|
369
|
+
if (sacred.top > bounds.top && bounds.top < MIN_NODE_SPACING) {
|
|
370
|
+
sacredSet.delete(sacredUuid);
|
|
371
|
+
sacredSet.add(uuid);
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
349
377
|
// Seed the queue with non-sacred nodes that overlap any sacred node
|
|
350
378
|
const queue: string[] = [];
|
|
351
379
|
const inQueue = new Set<string>();
|
|
@@ -387,11 +415,48 @@ export const calculateReflowPositions = (
|
|
|
387
415
|
|
|
388
416
|
if (fixedOverlaps.length === 0) continue;
|
|
389
417
|
|
|
390
|
-
//
|
|
418
|
+
// Determine direction constraints and axis bias from sacred node overlaps
|
|
419
|
+
const sacredOverlaps = fixedOverlaps.filter((f) => sacredSet.has(f.uuid));
|
|
420
|
+
const allowedDirections: Direction[] = [...DIRECTIONS];
|
|
421
|
+
let axisBias: 'vertical' | 'horizontal' | null = null;
|
|
422
|
+
|
|
423
|
+
if (sacredOverlaps.length > 0) {
|
|
424
|
+
// Rule 1: don't move a lower node above the sacred node
|
|
425
|
+
// Rule 2: don't move a right-of node to the left of the sacred node
|
|
426
|
+
for (const sacred of sacredOverlaps) {
|
|
427
|
+
if (collider.top > sacred.top) {
|
|
428
|
+
const idx = allowedDirections.indexOf('up');
|
|
429
|
+
if (idx !== -1) allowedDirections.splice(idx, 1);
|
|
430
|
+
}
|
|
431
|
+
if (collider.left > sacred.left) {
|
|
432
|
+
const idx = allowedDirections.indexOf('left');
|
|
433
|
+
if (idx !== -1) allowedDirections.splice(idx, 1);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Rule 3: bias direction based on overlap shape
|
|
438
|
+
let totalOverlapWidth = 0;
|
|
439
|
+
let totalOverlapHeight = 0;
|
|
440
|
+
for (const sacred of sacredOverlaps) {
|
|
441
|
+
totalOverlapWidth +=
|
|
442
|
+
Math.min(collider.right, sacred.right) -
|
|
443
|
+
Math.max(collider.left, sacred.left);
|
|
444
|
+
totalOverlapHeight +=
|
|
445
|
+
Math.min(collider.bottom, sacred.bottom) -
|
|
446
|
+
Math.max(collider.top, sacred.top);
|
|
447
|
+
}
|
|
448
|
+
if (totalOverlapWidth > totalOverlapHeight) {
|
|
449
|
+
axisBias = 'vertical'; // wide overlap = nodes stacked = prefer up/down
|
|
450
|
+
} else if (totalOverlapHeight > totalOverlapWidth) {
|
|
451
|
+
axisBias = 'horizontal'; // tall overlap = nodes side-by-side = prefer left/right
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Try each allowed direction, pick the one with least disruption
|
|
391
456
|
let bestPos: { left: number; top: number } | null = null;
|
|
392
457
|
let bestScore = Infinity;
|
|
393
458
|
|
|
394
|
-
for (const dir of
|
|
459
|
+
for (const dir of allowedDirections) {
|
|
395
460
|
const candidate = computeDirectionalClearance(
|
|
396
461
|
collider,
|
|
397
462
|
fixedOverlaps,
|
|
@@ -423,7 +488,21 @@ export const calculateReflowPositions = (
|
|
|
423
488
|
const distance =
|
|
424
489
|
Math.abs(candidate.left - collider.left) +
|
|
425
490
|
Math.abs(candidate.top - collider.top);
|
|
426
|
-
|
|
491
|
+
|
|
492
|
+
// When colliding with sacred nodes, use axis bias scoring;
|
|
493
|
+
// for cascading collisions (no sacred overlap), use original scoring
|
|
494
|
+
let score: number;
|
|
495
|
+
if (sacredOverlaps.length > 0) {
|
|
496
|
+
const isVerticalDir = dir === 'up' || dir === 'down';
|
|
497
|
+
const axisMatch =
|
|
498
|
+
axisBias === null ||
|
|
499
|
+
(axisBias === 'vertical' && isVerticalDir) ||
|
|
500
|
+
(axisBias === 'horizontal' && !isVerticalDir);
|
|
501
|
+
const axisPenalty = axisMatch ? 0 : 5000;
|
|
502
|
+
score = cascadeCount * 2000 + axisPenalty + distance;
|
|
503
|
+
} else {
|
|
504
|
+
score = cascadeCount * 10000 + distance;
|
|
505
|
+
}
|
|
427
506
|
|
|
428
507
|
if (score < bestScore) {
|
|
429
508
|
bestScore = score;
|
package/src/form/ArrayEditor.ts
CHANGED
|
@@ -648,13 +648,15 @@ export class TembaArrayEditor extends BaseListEditor<ListItem> {
|
|
|
648
648
|
}
|
|
649
649
|
|
|
650
650
|
.removable .remove-btn {
|
|
651
|
-
|
|
651
|
+
opacity: 0.3;
|
|
652
652
|
cursor: default;
|
|
653
|
+
pointer-events: none;
|
|
653
654
|
}
|
|
654
655
|
|
|
655
656
|
.removable .drag-handle {
|
|
656
|
-
|
|
657
|
+
opacity: 0.3;
|
|
657
658
|
cursor: default;
|
|
659
|
+
pointer-events: none;
|
|
658
660
|
}
|
|
659
661
|
|
|
660
662
|
.drag-handle {
|
|
@@ -8,8 +8,11 @@ import {
|
|
|
8
8
|
CheckboxFieldConfig,
|
|
9
9
|
MessageEditorFieldConfig,
|
|
10
10
|
KeyValueFieldConfig,
|
|
11
|
-
ArrayFieldConfig
|
|
11
|
+
ArrayFieldConfig,
|
|
12
|
+
MediaFieldConfig
|
|
12
13
|
} from '../flow/types';
|
|
14
|
+
import { Attachment } from '../interfaces';
|
|
15
|
+
import { DEFAULT_MEDIA_ENDPOINT } from '../utils';
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* FieldRenderer provides a consistent way to render field configurations
|
|
@@ -90,6 +93,14 @@ export class FieldRenderer {
|
|
|
90
93
|
context
|
|
91
94
|
);
|
|
92
95
|
|
|
96
|
+
case 'media':
|
|
97
|
+
return FieldRenderer.renderMedia(
|
|
98
|
+
fieldName,
|
|
99
|
+
config as MediaFieldConfig,
|
|
100
|
+
value,
|
|
101
|
+
context
|
|
102
|
+
);
|
|
103
|
+
|
|
93
104
|
default:
|
|
94
105
|
return html`<div>Unsupported field type: ${(config as any).type}</div>`;
|
|
95
106
|
}
|
|
@@ -345,6 +356,13 @@ export class FieldRenderer {
|
|
|
345
356
|
} = context;
|
|
346
357
|
|
|
347
358
|
return html`<div class="form-field">
|
|
359
|
+
${config.helpText
|
|
360
|
+
? html`<div
|
|
361
|
+
style="color: #666; font-size: 13px; margin-bottom: 14px;"
|
|
362
|
+
>
|
|
363
|
+
${config.helpText}
|
|
364
|
+
</div>`
|
|
365
|
+
: ''}
|
|
348
366
|
<temba-array-editor
|
|
349
367
|
name="${fieldName}"
|
|
350
368
|
.label="${showLabel ? config.label : ''}"
|
|
@@ -367,6 +385,58 @@ export class FieldRenderer {
|
|
|
367
385
|
</div>`;
|
|
368
386
|
}
|
|
369
387
|
|
|
388
|
+
private static urlToAttachments(url: string): Attachment[] {
|
|
389
|
+
if (!url || !url.trim()) return [];
|
|
390
|
+
const filename = url.split('/').pop() || 'recording';
|
|
391
|
+
const ext = filename.split('.').pop()?.toLowerCase() || '';
|
|
392
|
+
const contentTypes: Record<string, string> = {
|
|
393
|
+
mp3: 'audio/mpeg',
|
|
394
|
+
wav: 'audio/wav',
|
|
395
|
+
ogg: 'audio/ogg',
|
|
396
|
+
m4a: 'audio/mp4'
|
|
397
|
+
};
|
|
398
|
+
return [
|
|
399
|
+
{
|
|
400
|
+
uuid: '',
|
|
401
|
+
content_type: contentTypes[ext] || 'audio/mpeg',
|
|
402
|
+
url,
|
|
403
|
+
filename,
|
|
404
|
+
size: 0,
|
|
405
|
+
error: ''
|
|
406
|
+
}
|
|
407
|
+
];
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
private static renderMedia(
|
|
411
|
+
fieldName: string,
|
|
412
|
+
config: MediaFieldConfig,
|
|
413
|
+
value: any,
|
|
414
|
+
context: FieldRenderContext
|
|
415
|
+
): TemplateResult {
|
|
416
|
+
const { onChange, showLabel = true } = context;
|
|
417
|
+
const endpoint = config.endpoint || DEFAULT_MEDIA_ENDPOINT;
|
|
418
|
+
const attachments = FieldRenderer.urlToAttachments(value);
|
|
419
|
+
|
|
420
|
+
return html`
|
|
421
|
+
<div>
|
|
422
|
+
${showLabel && config.label
|
|
423
|
+
? html`<label
|
|
424
|
+
style="margin-bottom: 5px; margin-left: 4px; display: block; font-weight: 400; font-size: var(--label-size); letter-spacing: 0.05em; line-height: normal; color: var(--color-label, #777);"
|
|
425
|
+
>${config.label}</label
|
|
426
|
+
>`
|
|
427
|
+
: ''}
|
|
428
|
+
<temba-media-picker
|
|
429
|
+
name="${fieldName}"
|
|
430
|
+
accept="${config.accept || ''}"
|
|
431
|
+
endpoint="${endpoint}"
|
|
432
|
+
max="1"
|
|
433
|
+
.attachments="${attachments}"
|
|
434
|
+
@change="${onChange || (() => {})}"
|
|
435
|
+
></temba-media-picker>
|
|
436
|
+
</div>
|
|
437
|
+
`;
|
|
438
|
+
}
|
|
439
|
+
|
|
370
440
|
private static renderMessageEditor(
|
|
371
441
|
fieldName: string,
|
|
372
442
|
config: MessageEditorFieldConfig,
|
package/src/interfaces.ts
CHANGED
|
@@ -304,5 +304,6 @@ export enum CustomEventType {
|
|
|
304
304
|
NodeSaved = 'temba-node-saved',
|
|
305
305
|
NodeEditCancelled = 'temba-node-edit-cancelled',
|
|
306
306
|
FollowSimulation = 'temba-follow-simulation',
|
|
307
|
-
ContactClicked = 'temba-contact-clicked'
|
|
307
|
+
ContactClicked = 'temba-contact-clicked',
|
|
308
|
+
ShowIssue = 'temba-show-issue'
|
|
308
309
|
}
|
package/src/layout/Dialog.ts
CHANGED
|
@@ -96,7 +96,7 @@ export class Dialog extends ResizeElement {
|
|
|
96
96
|
|
|
97
97
|
.dialog-mask .dialog-container {
|
|
98
98
|
position: relative;
|
|
99
|
-
transition: transform var(--transition-speed)
|
|
99
|
+
transition: transform var(--transition-speed) ease-in-out,
|
|
100
100
|
opacity ease-in-out calc(var(--transition-speed) - 50ms);
|
|
101
101
|
border-radius: var(--curvature);
|
|
102
102
|
box-shadow: 0px 0px 2px 4px rgba(0, 0, 0, 0.06);
|
|
@@ -111,6 +111,7 @@ export class Dialog extends ResizeElement {
|
|
|
111
111
|
.dialog-body {
|
|
112
112
|
background: #fff;
|
|
113
113
|
overflow-y: auto;
|
|
114
|
+
overflow-x: hidden;
|
|
114
115
|
flex-grow: 1;
|
|
115
116
|
}
|
|
116
117
|
|
|
@@ -124,7 +125,7 @@ export class Dialog extends ResizeElement {
|
|
|
124
125
|
}
|
|
125
126
|
|
|
126
127
|
.dialog-mask.dialog-animation-end .dialog-container {
|
|
127
|
-
transform: scale(1) !important;
|
|
128
|
+
transform: scale(1) translate(0, 0) !important;
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
.dialog-mask.dialog-ready .dialog-container {
|
|
@@ -253,6 +254,12 @@ export class Dialog extends ResizeElement {
|
|
|
253
254
|
@property({ attribute: false })
|
|
254
255
|
onButtonClicked: (button: Button) => void;
|
|
255
256
|
|
|
257
|
+
@property({ type: Number })
|
|
258
|
+
originX: number | null = null;
|
|
259
|
+
|
|
260
|
+
@property({ type: Number })
|
|
261
|
+
originY: number | null = null;
|
|
262
|
+
|
|
256
263
|
scrollOffset: any = 0;
|
|
257
264
|
|
|
258
265
|
public constructor() {
|
|
@@ -289,11 +296,49 @@ export class Dialog extends ResizeElement {
|
|
|
289
296
|
const body = document.querySelector('body');
|
|
290
297
|
|
|
291
298
|
if (this.open) {
|
|
292
|
-
this.
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
299
|
+
if (this.originX != null && this.originY != null) {
|
|
300
|
+
// Spring-from-origin animation: measure final position, then
|
|
301
|
+
// set initial transform at click point and transition to center
|
|
302
|
+
const ox = this.originX;
|
|
303
|
+
const oy = this.originY;
|
|
304
|
+
this.originX = null;
|
|
305
|
+
this.originY = null;
|
|
306
|
+
|
|
307
|
+
requestAnimationFrame(() => {
|
|
308
|
+
const container = this.shadowRoot?.querySelector(
|
|
309
|
+
'.dialog-container'
|
|
310
|
+
) as HTMLElement;
|
|
311
|
+
if (container) {
|
|
312
|
+
const rect = container.getBoundingClientRect();
|
|
313
|
+
const cx = rect.left + rect.width / 2;
|
|
314
|
+
const cy = rect.top + rect.height / 2;
|
|
315
|
+
const dx = ox - cx;
|
|
316
|
+
const dy = oy - cy;
|
|
317
|
+
|
|
318
|
+
// Disable transition so we can set the start position instantly
|
|
319
|
+
container.style.transition = 'none';
|
|
320
|
+
container.style.transform = `translate(${dx}px, ${dy}px) scale(0.2)`;
|
|
321
|
+
// Force reflow to register the start position
|
|
322
|
+
container.getBoundingClientRect();
|
|
323
|
+
|
|
324
|
+
// Re-enable transition and trigger animation to final position
|
|
325
|
+
container.style.transition = '';
|
|
326
|
+
this.animationEnd = true;
|
|
327
|
+
window.setTimeout(() => {
|
|
328
|
+
this.ready = true;
|
|
329
|
+
this.animationEnd = false;
|
|
330
|
+
container.style.transform = '';
|
|
331
|
+
}, 400);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
} else {
|
|
335
|
+
// Default animation (no origin)
|
|
336
|
+
this.animationEnd = true;
|
|
337
|
+
window.setTimeout(() => {
|
|
338
|
+
this.ready = true;
|
|
339
|
+
this.animationEnd = false;
|
|
340
|
+
}, 400);
|
|
341
|
+
}
|
|
297
342
|
|
|
298
343
|
this.scrollOffset = -document.documentElement.scrollTop;
|
|
299
344
|
body.style.position = 'fixed';
|
package/src/layout/Modax.ts
CHANGED
|
@@ -148,9 +148,22 @@ export class Modax extends RapidElement {
|
|
|
148
148
|
|
|
149
149
|
@property({ type: Boolean })
|
|
150
150
|
suspendSubmit = false;
|
|
151
|
+
|
|
152
|
+
@property({ type: Number })
|
|
153
|
+
originX: number | null = null;
|
|
154
|
+
|
|
155
|
+
@property({ type: Number })
|
|
156
|
+
originY: number | null = null;
|
|
157
|
+
|
|
151
158
|
// private cancelToken: CancelTokenSource;
|
|
152
159
|
|
|
153
|
-
private handleSlotClicked(): void {
|
|
160
|
+
private handleSlotClicked(event: MouseEvent): void {
|
|
161
|
+
const el = event.currentTarget as Element;
|
|
162
|
+
if (el) {
|
|
163
|
+
const rect = el.getBoundingClientRect();
|
|
164
|
+
this.originX = rect.left + rect.width / 2;
|
|
165
|
+
this.originY = rect.top;
|
|
166
|
+
}
|
|
154
167
|
this.open = true;
|
|
155
168
|
}
|
|
156
169
|
|
|
@@ -173,6 +186,8 @@ export class Modax extends RapidElement {
|
|
|
173
186
|
// clear the modal body out when closed, note that js functions declared on the
|
|
174
187
|
// window will hang around
|
|
175
188
|
this.setBody('');
|
|
189
|
+
this.originX = null;
|
|
190
|
+
this.originY = null;
|
|
176
191
|
}
|
|
177
192
|
}
|
|
178
193
|
}
|
|
@@ -441,11 +456,13 @@ export class Modax extends RapidElement {
|
|
|
441
456
|
.header=${this.header}
|
|
442
457
|
.buttons=${this.buttons}
|
|
443
458
|
?open=${this.open}
|
|
444
|
-
?loading=${this.fetching}
|
|
459
|
+
?loading=${this.fetching && this.originX == null}
|
|
445
460
|
?submitting=${this.submitting}
|
|
446
461
|
?destructive=${this.isDestructive()}
|
|
447
462
|
?noFocus=${true}
|
|
448
463
|
?disabled=${this.disabled}
|
|
464
|
+
.originX=${this.originX}
|
|
465
|
+
.originY=${this.originY}
|
|
449
466
|
@temba-button-clicked=${this.handleDialogClick.bind(this)}
|
|
450
467
|
@temba-dialog-hidden=${this.handleDialogHidden.bind(this)}
|
|
451
468
|
>
|
package/src/list/ContentMenu.ts
CHANGED
|
@@ -159,8 +159,22 @@ export class ContentMenu extends RapidElement {
|
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
private getTopCenter(el: Element): { x: number; y: number } {
|
|
163
|
+
const rect = el.getBoundingClientRect();
|
|
164
|
+
return { x: rect.left + rect.width / 2, y: rect.top };
|
|
165
|
+
}
|
|
166
|
+
|
|
162
167
|
private handleItemClicked(item: ContentMenuItem, event: MouseEvent) {
|
|
163
|
-
|
|
168
|
+
const el = event.currentTarget as Element;
|
|
169
|
+
const origin = el
|
|
170
|
+
? this.getTopCenter(el)
|
|
171
|
+
: { x: event.clientX, y: event.clientY };
|
|
172
|
+
this.fireCustomEvent(CustomEventType.Selection, {
|
|
173
|
+
item,
|
|
174
|
+
event,
|
|
175
|
+
originX: origin.x,
|
|
176
|
+
originY: origin.y
|
|
177
|
+
});
|
|
164
178
|
}
|
|
165
179
|
|
|
166
180
|
public render(): TemplateResult {
|
package/src/locales/es.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
|
|
2
|
+
// Do not modify this file by hand!
|
|
3
|
+
// Re-generate this file by running lit-localize
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/* eslint-disable no-irregular-whitespace */
|
|
9
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
10
|
+
|
|
11
|
+
export const templates = {
|
|
12
|
+
'scf1453991c986b25': `Tab para completar, enter para seleccionar`,
|
|
13
|
+
's73b4d70c02f4b4e0': `No options`,
|
|
14
|
+
's8f02e3a18ffc083a': `Are not currently in a flow`,
|
|
15
|
+
's638236250662c6b3': `Have sent a message in the last`,
|
|
16
|
+
's4788ee206c4570c7': `Have not started this flow in the last 90 days`,
|
|
17
|
+
};
|
|
18
|
+
|
package/src/locales/fr.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
|
|
2
|
+
// Do not modify this file by hand!
|
|
3
|
+
// Re-generate this file by running lit-localize
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/* eslint-disable no-irregular-whitespace */
|
|
9
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
10
|
+
|
|
11
|
+
export const templates = {
|
|
12
|
+
's73b4d70c02f4b4e0': `No options`,
|
|
13
|
+
'scf1453991c986b25': `Tab to complete, enter to select`,
|
|
14
|
+
's8f02e3a18ffc083a': `Are not currently in a flow`,
|
|
15
|
+
's638236250662c6b3': `Have sent a message in the last`,
|
|
16
|
+
's4788ee206c4570c7': `Have not started this flow in the last 90 days`,
|
|
17
|
+
};
|
|
18
|
+
|
|
@@ -10,9 +10,18 @@ export const sourceLocale = `en`;
|
|
|
10
10
|
* The other locale codes that this application is localized into. Sorted
|
|
11
11
|
* lexicographically.
|
|
12
12
|
*/
|
|
13
|
-
export const targetLocales = [
|
|
13
|
+
export const targetLocales = [
|
|
14
|
+
`es`,
|
|
15
|
+
`fr`,
|
|
16
|
+
`pt`,
|
|
17
|
+
] as const;
|
|
14
18
|
|
|
15
19
|
/**
|
|
16
20
|
* All valid project locale codes. Sorted lexicographically.
|
|
17
21
|
*/
|
|
18
|
-
export const allLocales = [
|
|
22
|
+
export const allLocales = [
|
|
23
|
+
`en`,
|
|
24
|
+
`es`,
|
|
25
|
+
`fr`,
|
|
26
|
+
`pt`,
|
|
27
|
+
] as const;
|
package/src/locales/pt.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
|
|
2
|
+
// Do not modify this file by hand!
|
|
3
|
+
// Re-generate this file by running lit-localize
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/* eslint-disable no-irregular-whitespace */
|
|
9
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
10
|
+
|
|
11
|
+
export const templates = {
|
|
12
|
+
's73b4d70c02f4b4e0': `No options`,
|
|
13
|
+
'scf1453991c986b25': `Tab to complete, enter to select`,
|
|
14
|
+
's8f02e3a18ffc083a': `Are not currently in a flow`,
|
|
15
|
+
's638236250662c6b3': `Have sent a message in the last`,
|
|
16
|
+
's4788ee206c4570c7': `Have not started this flow in the last 90 days`,
|
|
17
|
+
};
|
|
18
|
+
|
|
@@ -5,6 +5,8 @@ import { css, PropertyValueMap } from 'lit';
|
|
|
5
5
|
import { property } from 'lit/decorators.js';
|
|
6
6
|
import { postJSON, fromCookie, generateUUIDv7 } from '../utils';
|
|
7
7
|
import { getStore } from '../store/Store';
|
|
8
|
+
import { AppState, fromStore, zustand } from '../store/AppState';
|
|
9
|
+
import { FlowDefinition } from '../store/flow-definition';
|
|
8
10
|
import { CustomEventType } from '../interfaces';
|
|
9
11
|
import { Chat, ContactEvent, MessageType } from '../display/Chat';
|
|
10
12
|
import { Events, renderEvent } from '../events/eventRenderers';
|
|
@@ -148,6 +150,10 @@ const SIMULATOR_SIZES: Record<string, SimulatorSize> = {
|
|
|
148
150
|
export class Simulator extends RapidElement {
|
|
149
151
|
static get styles() {
|
|
150
152
|
return css`
|
|
153
|
+
temba-floating-tab {
|
|
154
|
+
--floating-tab-right: 15px;
|
|
155
|
+
}
|
|
156
|
+
|
|
151
157
|
:host {
|
|
152
158
|
/* size-specific dimensions are set dynamically via inline styles */
|
|
153
159
|
--phone-width: 300px;
|
|
@@ -688,6 +694,12 @@ export class Simulator extends RapidElement {
|
|
|
688
694
|
`;
|
|
689
695
|
}
|
|
690
696
|
|
|
697
|
+
@fromStore(zustand, (state: AppState) => state.flowDefinition)
|
|
698
|
+
private definition!: FlowDefinition;
|
|
699
|
+
|
|
700
|
+
@fromStore(zustand, (state: AppState) => state.viewingRevision)
|
|
701
|
+
private viewingRevision!: boolean;
|
|
702
|
+
|
|
691
703
|
@property({ type: String })
|
|
692
704
|
flow = '';
|
|
693
705
|
|
|
@@ -1117,8 +1129,12 @@ export class Simulator extends RapidElement {
|
|
|
1117
1129
|
continue;
|
|
1118
1130
|
}
|
|
1119
1131
|
|
|
1120
|
-
// skip msg_created events without a proper msg property
|
|
1121
|
-
if (
|
|
1132
|
+
// skip msg_created/ivr_created events without a proper msg property
|
|
1133
|
+
if (
|
|
1134
|
+
(rawEvent.type === 'msg_created' ||
|
|
1135
|
+
rawEvent.type === 'ivr_created') &&
|
|
1136
|
+
!(rawEvent as any).msg
|
|
1137
|
+
) {
|
|
1122
1138
|
continue;
|
|
1123
1139
|
}
|
|
1124
1140
|
|
|
@@ -1140,7 +1156,8 @@ export class Simulator extends RapidElement {
|
|
|
1140
1156
|
this.currentQuickReplies = (event as any).msg.quick_replies;
|
|
1141
1157
|
}
|
|
1142
1158
|
|
|
1143
|
-
const isMessage =
|
|
1159
|
+
const isMessage =
|
|
1160
|
+
event.type === 'msg_created' || event.type === 'ivr_created';
|
|
1144
1161
|
const msg = (event as any).msg;
|
|
1145
1162
|
|
|
1146
1163
|
// Check if the event should be displayed.
|
|
@@ -1717,6 +1734,10 @@ export class Simulator extends RapidElement {
|
|
|
1717
1734
|
}
|
|
1718
1735
|
|
|
1719
1736
|
protected render(): TemplateResult {
|
|
1737
|
+
if (this.viewingRevision || this.definition?.nodes.length === 0) {
|
|
1738
|
+
return html``;
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1720
1741
|
const config = this.sizeConfig;
|
|
1721
1742
|
|
|
1722
1743
|
// set CSS custom properties dynamically based on size
|
|
@@ -1934,7 +1955,7 @@ export class Simulator extends RapidElement {
|
|
|
1934
1955
|
icon="simulator"
|
|
1935
1956
|
label="Phone Simulator"
|
|
1936
1957
|
color="#10b981"
|
|
1937
|
-
order="
|
|
1958
|
+
order="4"
|
|
1938
1959
|
.hidden=${this.isVisible}
|
|
1939
1960
|
@temba-button-clicked=${this.handleShow}
|
|
1940
1961
|
></temba-floating-tab>
|