@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
|
@@ -8,11 +8,11 @@ import { RapidElement } from '../RapidElement';
|
|
|
8
8
|
import { repeat } from 'lit-html/directives/repeat.js';
|
|
9
9
|
import { CustomEventType } from '../interfaces';
|
|
10
10
|
import { generateUUID, postJSON, fetchResults, getClasses } from '../utils';
|
|
11
|
+
import { formatIssueMessage, getNodeBounds, calculateReflowPositions, snapToGrid } from './utils';
|
|
11
12
|
import { ACTION_CONFIG, NODE_CONFIG } from './config';
|
|
12
13
|
import { ACTION_GROUP_METADATA } from './types';
|
|
13
14
|
import { Plumber, calculateFlowchartPath, ARROW_LENGTH, ARROW_HALF_WIDTH, CURSOR_GAP } from './Plumber';
|
|
14
15
|
import { CanvasNode } from './CanvasNode';
|
|
15
|
-
import { getNodeBounds, calculateReflowPositions, snapToGrid } from './utils';
|
|
16
16
|
export function findNodeForExit(definition, exitUuid) {
|
|
17
17
|
for (const node of definition.nodes) {
|
|
18
18
|
const exit = node.exits.find((e) => e.uuid === exitUuid);
|
|
@@ -22,7 +22,7 @@ export function findNodeForExit(definition, exitUuid) {
|
|
|
22
22
|
}
|
|
23
23
|
return null;
|
|
24
24
|
}
|
|
25
|
-
const SAVE_QUIET_TIME =
|
|
25
|
+
const SAVE_QUIET_TIME = 2000;
|
|
26
26
|
const DRAG_THRESHOLD = 5;
|
|
27
27
|
const AUTO_TRANSLATE_MODELS_ENDPOINT = '/api/internal/llms.json';
|
|
28
28
|
// Offset for positioning dropped action node relative to mouse cursor
|
|
@@ -68,12 +68,23 @@ export class Editor extends RapidElement {
|
|
|
68
68
|
}
|
|
69
69
|
static get styles() {
|
|
70
70
|
return css `
|
|
71
|
+
#editor-container {
|
|
72
|
+
position: relative;
|
|
73
|
+
flex: 1;
|
|
74
|
+
display: flex;
|
|
75
|
+
min-height: 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
71
78
|
#editor {
|
|
72
79
|
overflow: scroll;
|
|
73
80
|
flex: 1;
|
|
74
81
|
-webkit-font-smoothing: antialiased;
|
|
75
82
|
}
|
|
76
83
|
|
|
84
|
+
temba-floating-tab {
|
|
85
|
+
--floating-tab-right: 15px;
|
|
86
|
+
}
|
|
87
|
+
|
|
77
88
|
#grid {
|
|
78
89
|
position: relative;
|
|
79
90
|
background-color: #f9f9f9;
|
|
@@ -207,7 +218,7 @@ export class Editor extends RapidElement {
|
|
|
207
218
|
font-weight: 600;
|
|
208
219
|
line-height: 0.9;
|
|
209
220
|
cursor: pointer;
|
|
210
|
-
z-index:
|
|
221
|
+
z-index: 10;
|
|
211
222
|
pointer-events: auto;
|
|
212
223
|
white-space: nowrap;
|
|
213
224
|
user-select: none;
|
|
@@ -522,6 +533,91 @@ export class Editor extends RapidElement {
|
|
|
522
533
|
color: #9ca3af;
|
|
523
534
|
white-space: nowrap;
|
|
524
535
|
}
|
|
536
|
+
|
|
537
|
+
.issue-list-item {
|
|
538
|
+
display: flex;
|
|
539
|
+
align-items: center;
|
|
540
|
+
gap: 8px;
|
|
541
|
+
padding: 8px;
|
|
542
|
+
border-radius: 4px;
|
|
543
|
+
cursor: pointer;
|
|
544
|
+
font-size: 13px;
|
|
545
|
+
color: #333;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
.issue-list-item:hover {
|
|
549
|
+
background: #fff5f5;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.issue-list-item temba-icon {
|
|
553
|
+
color: tomato;
|
|
554
|
+
flex-shrink: 0;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
.empty-flow {
|
|
558
|
+
position: sticky;
|
|
559
|
+
top: 80px;
|
|
560
|
+
left: 0;
|
|
561
|
+
right: 0;
|
|
562
|
+
height: 0;
|
|
563
|
+
display: flex;
|
|
564
|
+
justify-content: center;
|
|
565
|
+
pointer-events: none;
|
|
566
|
+
z-index: 50;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
.empty-flow-content {
|
|
570
|
+
display: flex;
|
|
571
|
+
flex-direction: column;
|
|
572
|
+
align-items: center;
|
|
573
|
+
gap: 16px;
|
|
574
|
+
text-align: center;
|
|
575
|
+
pointer-events: auto;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
.empty-flow-title {
|
|
579
|
+
font-size: 18px;
|
|
580
|
+
font-weight: 600;
|
|
581
|
+
color: #374151;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
.empty-flow-description {
|
|
585
|
+
font-size: 14px;
|
|
586
|
+
color: #6b7280;
|
|
587
|
+
max-width: 320px;
|
|
588
|
+
line-height: 1.5;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.empty-flow-button {
|
|
592
|
+
background: var(--color-primary-dark);
|
|
593
|
+
border: none;
|
|
594
|
+
color: #fff;
|
|
595
|
+
padding: 10px 20px;
|
|
596
|
+
border-radius: var(--curvature);
|
|
597
|
+
font-size: 14px;
|
|
598
|
+
font-weight: 600;
|
|
599
|
+
cursor: pointer;
|
|
600
|
+
transition: opacity 0.2s ease;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
.empty-flow-button:hover {
|
|
604
|
+
opacity: 0.9;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
.save-indicator {
|
|
608
|
+
position: absolute;
|
|
609
|
+
top: 8px;
|
|
610
|
+
right: 16px;
|
|
611
|
+
padding: 6px 10px;
|
|
612
|
+
z-index: 10000;
|
|
613
|
+
pointer-events: none;
|
|
614
|
+
opacity: 0;
|
|
615
|
+
transition: opacity 0.15s ease-in-out;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
.save-indicator.visible {
|
|
619
|
+
opacity: 1;
|
|
620
|
+
}
|
|
525
621
|
`;
|
|
526
622
|
}
|
|
527
623
|
constructor() {
|
|
@@ -550,6 +646,7 @@ export class Editor extends RapidElement {
|
|
|
550
646
|
// Canvas-relative source exit position (set at drag start)
|
|
551
647
|
this.connectionSourceX = null;
|
|
552
648
|
this.connectionSourceY = null;
|
|
649
|
+
this.issuesWindowHidden = true;
|
|
553
650
|
this.localizationWindowHidden = true;
|
|
554
651
|
this.translationFilters = {
|
|
555
652
|
categories: false
|
|
@@ -563,12 +660,15 @@ export class Editor extends RapidElement {
|
|
|
563
660
|
this.revisions = [];
|
|
564
661
|
this.viewingRevision = null;
|
|
565
662
|
this.isLoadingRevisions = false;
|
|
663
|
+
this.isSaving = false;
|
|
664
|
+
this.saveError = null;
|
|
566
665
|
this.preRevertState = null;
|
|
567
666
|
this.translationCache = new Map();
|
|
568
667
|
// NodeEditor state - handles both node and action editing
|
|
569
668
|
this.editingNode = null;
|
|
570
669
|
this.editingNodeUI = null;
|
|
571
670
|
this.editingAction = null;
|
|
671
|
+
this.dialogOrigin = null;
|
|
572
672
|
this.isCreatingNewNode = false;
|
|
573
673
|
this.pendingNodePosition = null;
|
|
574
674
|
// Canvas drop state for dragging actions to canvas
|
|
@@ -596,6 +696,7 @@ export class Editor extends RapidElement {
|
|
|
596
696
|
this.setupGlobalEventListeners();
|
|
597
697
|
if (changes.has('flow')) {
|
|
598
698
|
getStore().getState().fetchRevision(`/flow/revisions/${this.flow}`);
|
|
699
|
+
this.fetchRevisions();
|
|
599
700
|
}
|
|
600
701
|
this.plumber.on('connection:drag', (connection) => {
|
|
601
702
|
this.dragFromNodeId = connection.data.nodeId;
|
|
@@ -723,9 +824,14 @@ export class Editor extends RapidElement {
|
|
|
723
824
|
}
|
|
724
825
|
if (changes.has('dirtyDate')) {
|
|
725
826
|
if (this.dirtyDate) {
|
|
827
|
+
this.isSaving = true;
|
|
726
828
|
this.debouncedSave();
|
|
727
829
|
}
|
|
728
830
|
}
|
|
831
|
+
if (changes.has('saveError') && this.saveError) {
|
|
832
|
+
this.showSaveErrorDialog(this.saveError);
|
|
833
|
+
this.saveError = null;
|
|
834
|
+
}
|
|
729
835
|
if (changes.has('languageCode')) {
|
|
730
836
|
this.translationCache.clear();
|
|
731
837
|
}
|
|
@@ -767,11 +873,15 @@ export class Editor extends RapidElement {
|
|
|
767
873
|
}
|
|
768
874
|
saveChanges(definitionOverride) {
|
|
769
875
|
const definition = definitionOverride || this.definition;
|
|
770
|
-
|
|
876
|
+
this.isSaving = true;
|
|
771
877
|
return getStore()
|
|
772
878
|
.postJSON(`/flow/revisions/${this.flow}/`, definition)
|
|
773
879
|
.then((response) => {
|
|
774
880
|
var _b;
|
|
881
|
+
if (response.status < 200 || response.status >= 300) {
|
|
882
|
+
this.saveError = this.extractErrorMessage(response);
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
775
885
|
// Update flow info and revision with the response data
|
|
776
886
|
if (response.json) {
|
|
777
887
|
const state = getStore().getState();
|
|
@@ -781,16 +891,53 @@ export class Editor extends RapidElement {
|
|
|
781
891
|
if (((_b = response.json.revision) === null || _b === void 0 ? void 0 : _b.revision) !== undefined) {
|
|
782
892
|
state.setRevision(response.json.revision.revision);
|
|
783
893
|
}
|
|
784
|
-
//
|
|
785
|
-
|
|
786
|
-
this.fetchRevisions();
|
|
787
|
-
}
|
|
894
|
+
// Refresh revisions list so the tab visibility stays up to date
|
|
895
|
+
this.fetchRevisions();
|
|
788
896
|
}
|
|
897
|
+
getStore().getState().setDirtyDate(null);
|
|
789
898
|
})
|
|
790
899
|
.catch((error) => {
|
|
791
900
|
console.error('Failed to save flow:', error);
|
|
901
|
+
if (error instanceof Response) {
|
|
902
|
+
this.saveError = `Server error (${error.status}). Your changes have not been saved.`;
|
|
903
|
+
}
|
|
904
|
+
else {
|
|
905
|
+
this.saveError =
|
|
906
|
+
'Unable to reach the server. Please check your connection and try again.';
|
|
907
|
+
}
|
|
908
|
+
})
|
|
909
|
+
.finally(() => {
|
|
910
|
+
this.isSaving = false;
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
extractErrorMessage(response) {
|
|
914
|
+
if (response.json) {
|
|
915
|
+
if (typeof response.json.detail === 'string') {
|
|
916
|
+
return response.json.detail;
|
|
917
|
+
}
|
|
918
|
+
if (typeof response.json.error === 'string') {
|
|
919
|
+
return response.json.error;
|
|
920
|
+
}
|
|
921
|
+
if (typeof response.json.description === 'string') {
|
|
922
|
+
return response.json.description;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
return `Save failed with status ${response.status}.`;
|
|
926
|
+
}
|
|
927
|
+
showSaveErrorDialog(message) {
|
|
928
|
+
const dialog = document.createElement('temba-dialog');
|
|
929
|
+
dialog.header = 'Save Failed';
|
|
930
|
+
dialog.primaryButtonName = '';
|
|
931
|
+
dialog.cancelButtonName = 'Dismiss';
|
|
932
|
+
const content = document.createElement('div');
|
|
933
|
+
content.style.cssText = 'padding: 20px; font-size: 14px; line-height: 1.5;';
|
|
934
|
+
content.textContent = message;
|
|
935
|
+
dialog.appendChild(content);
|
|
936
|
+
document.body.appendChild(dialog);
|
|
937
|
+
dialog.open = true;
|
|
938
|
+
dialog.addEventListener('temba-dialog-hidden', () => {
|
|
939
|
+
document.body.removeChild(dialog);
|
|
792
940
|
});
|
|
793
|
-
getStore().getState().setDirtyDate(null);
|
|
794
941
|
}
|
|
795
942
|
startActivityFetching() {
|
|
796
943
|
// Don't start if simulator is active
|
|
@@ -822,6 +969,10 @@ export class Editor extends RapidElement {
|
|
|
822
969
|
}
|
|
823
970
|
const state = store.getState();
|
|
824
971
|
state.fetchActivity(activityEndpoint).then(() => {
|
|
972
|
+
// Guard against responses arriving after the editor is disconnected
|
|
973
|
+
if (!this.isConnected) {
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
825
976
|
// Schedule next fetch with exponential backoff (max 5 minutes)
|
|
826
977
|
this.activityInterval = Math.min(60000 * 5, this.activityInterval + 100);
|
|
827
978
|
if (this.activityTimer !== null) {
|
|
@@ -853,6 +1004,9 @@ export class Editor extends RapidElement {
|
|
|
853
1004
|
if (canvas) {
|
|
854
1005
|
canvas.removeEventListener('contextmenu', this.boundCanvasContextMenu);
|
|
855
1006
|
}
|
|
1007
|
+
// Clear all flow-specific data from the store so stale data
|
|
1008
|
+
// isn't briefly visible when a different flow is opened.
|
|
1009
|
+
zustand.getState().clearFlowData();
|
|
856
1010
|
}
|
|
857
1011
|
setupGlobalEventListeners() {
|
|
858
1012
|
document.addEventListener('mousemove', this.boundMouseMove);
|
|
@@ -915,6 +1069,7 @@ export class Editor extends RapidElement {
|
|
|
915
1069
|
return;
|
|
916
1070
|
if (this.isReadOnly())
|
|
917
1071
|
return;
|
|
1072
|
+
this.blurActiveContentEditable();
|
|
918
1073
|
const element = event.currentTarget;
|
|
919
1074
|
// Only start dragging if clicking on the element itself, not on exits or other interactive elements
|
|
920
1075
|
const target = event.target;
|
|
@@ -974,10 +1129,22 @@ export class Editor extends RapidElement {
|
|
|
974
1129
|
// We clicked on empty canvas space, start selection
|
|
975
1130
|
this.handleCanvasMouseDown(event);
|
|
976
1131
|
}
|
|
1132
|
+
blurActiveContentEditable() {
|
|
1133
|
+
var _b;
|
|
1134
|
+
let active = document.activeElement;
|
|
1135
|
+
while ((_b = active === null || active === void 0 ? void 0 : active.shadowRoot) === null || _b === void 0 ? void 0 : _b.activeElement) {
|
|
1136
|
+
active = active.shadowRoot.activeElement;
|
|
1137
|
+
}
|
|
1138
|
+
if (active instanceof HTMLElement &&
|
|
1139
|
+
active.getAttribute('contenteditable') === 'true') {
|
|
1140
|
+
active.blur();
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
977
1143
|
handleCanvasMouseDown(event) {
|
|
978
1144
|
var _b;
|
|
979
1145
|
if (this.isReadOnly())
|
|
980
1146
|
return;
|
|
1147
|
+
this.blurActiveContentEditable();
|
|
981
1148
|
const target = event.target;
|
|
982
1149
|
if (target.id === 'canvas' || target.id === 'grid') {
|
|
983
1150
|
// Ignore clicks on exits
|
|
@@ -1538,6 +1705,25 @@ export class Editor extends RapidElement {
|
|
|
1538
1705
|
});
|
|
1539
1706
|
}
|
|
1540
1707
|
}
|
|
1708
|
+
handleEmptyFlowClick(event) {
|
|
1709
|
+
const editor = this.querySelector('#editor');
|
|
1710
|
+
if (!editor)
|
|
1711
|
+
return;
|
|
1712
|
+
// Scroll to top-left
|
|
1713
|
+
editor.scrollTo({ left: 0, top: 0, behavior: 'smooth' });
|
|
1714
|
+
// Place node at top-left of the canvas
|
|
1715
|
+
const nodeLeft = 0;
|
|
1716
|
+
const nodeTop = 0;
|
|
1717
|
+
const canvasMenu = this.querySelector('temba-canvas-menu');
|
|
1718
|
+
if (canvasMenu) {
|
|
1719
|
+
const button = event.currentTarget;
|
|
1720
|
+
const rect = button.getBoundingClientRect();
|
|
1721
|
+
const menuWidth = 265;
|
|
1722
|
+
const menuX = rect.left + rect.width / 2 - menuWidth / 2;
|
|
1723
|
+
const menuY = rect.bottom + 8;
|
|
1724
|
+
canvasMenu.show(menuX, menuY, { x: nodeLeft, y: nodeTop }, false);
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1541
1727
|
handleCanvasMenuSelection(event) {
|
|
1542
1728
|
const selection = event.detail;
|
|
1543
1729
|
const store = getStore();
|
|
@@ -1692,6 +1878,10 @@ export class Editor extends RapidElement {
|
|
|
1692
1878
|
handleActionEditRequested(event) {
|
|
1693
1879
|
// For action editing, we set the action and find the corresponding node
|
|
1694
1880
|
this.editingAction = event.detail.action;
|
|
1881
|
+
this.dialogOrigin =
|
|
1882
|
+
event.detail.originX != null
|
|
1883
|
+
? { x: event.detail.originX, y: event.detail.originY }
|
|
1884
|
+
: null;
|
|
1695
1885
|
// Find the node that contains this action
|
|
1696
1886
|
const nodeUuid = event.detail.nodeUuid;
|
|
1697
1887
|
const node = this.definition.nodes.find((n) => n.uuid === nodeUuid);
|
|
@@ -1727,6 +1917,10 @@ export class Editor extends RapidElement {
|
|
|
1727
1917
|
handleNodeEditRequested(event) {
|
|
1728
1918
|
this.editingNode = event.detail.node;
|
|
1729
1919
|
this.editingNodeUI = event.detail.nodeUI;
|
|
1920
|
+
this.dialogOrigin =
|
|
1921
|
+
event.detail.originX != null
|
|
1922
|
+
? { x: event.detail.originX, y: event.detail.originY }
|
|
1923
|
+
: null;
|
|
1730
1924
|
}
|
|
1731
1925
|
handleNodeDeleted(event) {
|
|
1732
1926
|
const nodeUuid = event.detail.uuid;
|
|
@@ -1797,6 +1991,7 @@ export class Editor extends RapidElement {
|
|
|
1797
1991
|
this.editingNode = null;
|
|
1798
1992
|
this.editingNodeUI = null;
|
|
1799
1993
|
this.editingAction = null;
|
|
1994
|
+
this.dialogOrigin = null;
|
|
1800
1995
|
}
|
|
1801
1996
|
handleActionEditCanceled() {
|
|
1802
1997
|
// If we were creating a new node, just discard it
|
|
@@ -2253,6 +2448,7 @@ export class Editor extends RapidElement {
|
|
|
2253
2448
|
}
|
|
2254
2449
|
this.localizationWindowHidden = false;
|
|
2255
2450
|
this.revisionsWindowHidden = true;
|
|
2451
|
+
this.issuesWindowHidden = true;
|
|
2256
2452
|
const alreadySelected = languages.some((lang) => lang.code === this.languageCode);
|
|
2257
2453
|
if (!alreadySelected) {
|
|
2258
2454
|
this.handleLanguageChange(languages[0].code);
|
|
@@ -2460,11 +2656,42 @@ export class Editor extends RapidElement {
|
|
|
2460
2656
|
}
|
|
2461
2657
|
this.autoTranslating = false;
|
|
2462
2658
|
}
|
|
2659
|
+
handleIssuesTabClick() {
|
|
2660
|
+
this.issuesWindowHidden = false;
|
|
2661
|
+
this.revisionsWindowHidden = true;
|
|
2662
|
+
this.localizationWindowHidden = true;
|
|
2663
|
+
}
|
|
2664
|
+
handleIssuesWindowClosed() {
|
|
2665
|
+
this.issuesWindowHidden = true;
|
|
2666
|
+
}
|
|
2667
|
+
handleIssueItemClick(issue) {
|
|
2668
|
+
var _b;
|
|
2669
|
+
const issuesWindow = document.getElementById('issues-window');
|
|
2670
|
+
issuesWindow === null || issuesWindow === void 0 ? void 0 : issuesWindow.handleClose();
|
|
2671
|
+
this.issuesWindowHidden = true;
|
|
2672
|
+
this.focusNode(issue.node_uuid);
|
|
2673
|
+
const node = this.definition.nodes.find((n) => n.uuid === issue.node_uuid);
|
|
2674
|
+
if (!node)
|
|
2675
|
+
return;
|
|
2676
|
+
if (issue.action_uuid) {
|
|
2677
|
+
const action = (_b = node.actions) === null || _b === void 0 ? void 0 : _b.find((a) => a.uuid === issue.action_uuid);
|
|
2678
|
+
if (action) {
|
|
2679
|
+
this.editingAction = action;
|
|
2680
|
+
this.editingNode = node;
|
|
2681
|
+
this.editingNodeUI = this.definition._ui.nodes[issue.node_uuid];
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
else {
|
|
2685
|
+
this.editingNode = node;
|
|
2686
|
+
this.editingNodeUI = this.definition._ui.nodes[issue.node_uuid];
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2463
2689
|
handleRevisionsTabClick() {
|
|
2464
2690
|
if (this.revisionsWindowHidden) {
|
|
2465
2691
|
this.fetchRevisions();
|
|
2466
2692
|
this.revisionsWindowHidden = false;
|
|
2467
|
-
this.
|
|
2693
|
+
this.issuesWindowHidden = true;
|
|
2694
|
+
this.localizationWindowHidden = true;
|
|
2468
2695
|
}
|
|
2469
2696
|
}
|
|
2470
2697
|
handleRevisionsWindowClosed() {
|
|
@@ -2565,14 +2792,61 @@ export class Editor extends RapidElement {
|
|
|
2565
2792
|
// Fetch the latest version of the flow to ensure the store is up to date
|
|
2566
2793
|
getStore().getState().fetchRevision(`/flow/revisions/${this.flow}`);
|
|
2567
2794
|
}
|
|
2795
|
+
renderIssuesTab() {
|
|
2796
|
+
var _b;
|
|
2797
|
+
if (!((_b = this.flowIssues) === null || _b === void 0 ? void 0 : _b.length) || !this.revisionsWindowHidden)
|
|
2798
|
+
return '';
|
|
2799
|
+
return html `
|
|
2800
|
+
<temba-floating-tab
|
|
2801
|
+
id="issues-tab"
|
|
2802
|
+
icon="alert_warning"
|
|
2803
|
+
label="Flow Issues"
|
|
2804
|
+
color="tomato"
|
|
2805
|
+
order="1"
|
|
2806
|
+
.hidden=${!this.issuesWindowHidden}
|
|
2807
|
+
@temba-button-clicked=${this.handleIssuesTabClick}
|
|
2808
|
+
></temba-floating-tab>
|
|
2809
|
+
`;
|
|
2810
|
+
}
|
|
2811
|
+
renderIssuesWindow() {
|
|
2812
|
+
var _b;
|
|
2813
|
+
if (!((_b = this.flowIssues) === null || _b === void 0 ? void 0 : _b.length))
|
|
2814
|
+
return '';
|
|
2815
|
+
return html `
|
|
2816
|
+
<temba-floating-window
|
|
2817
|
+
id="issues-window"
|
|
2818
|
+
header="Flow Issues"
|
|
2819
|
+
.width=${360}
|
|
2820
|
+
.maxHeight=${600}
|
|
2821
|
+
.top=${75}
|
|
2822
|
+
color="tomato"
|
|
2823
|
+
.hidden=${this.issuesWindowHidden}
|
|
2824
|
+
@temba-dialog-hidden=${this.handleIssuesWindowClosed}
|
|
2825
|
+
>
|
|
2826
|
+
<div style="display:flex; flex-direction:column; gap:2px;">
|
|
2827
|
+
${this.flowIssues.map((issue) => html `
|
|
2828
|
+
<div
|
|
2829
|
+
class="issue-list-item"
|
|
2830
|
+
@click=${() => this.handleIssueItemClick(issue)}
|
|
2831
|
+
>
|
|
2832
|
+
<temba-icon name="alert_warning" size="1.2"></temba-icon>
|
|
2833
|
+
<span>${formatIssueMessage(issue)}</span>
|
|
2834
|
+
</div>
|
|
2835
|
+
`)}
|
|
2836
|
+
</div>
|
|
2837
|
+
</temba-floating-window>
|
|
2838
|
+
`;
|
|
2839
|
+
}
|
|
2568
2840
|
renderRevisionsTab() {
|
|
2841
|
+
if (this.revisions.length <= 1)
|
|
2842
|
+
return '';
|
|
2569
2843
|
return html `
|
|
2570
2844
|
<temba-floating-tab
|
|
2571
2845
|
id="revisions-tab"
|
|
2572
2846
|
icon="revisions"
|
|
2573
2847
|
label="Revisions"
|
|
2574
2848
|
color="rgb(142, 94, 167)"
|
|
2575
|
-
order="
|
|
2849
|
+
order="2"
|
|
2576
2850
|
.hidden=${!this.revisionsWindowHidden && this.localizationWindowHidden}
|
|
2577
2851
|
@temba-button-clicked=${this.handleRevisionsTabClick}
|
|
2578
2852
|
></temba-floating-tab>
|
|
@@ -2833,6 +3107,11 @@ export class Editor extends RapidElement {
|
|
|
2833
3107
|
`;
|
|
2834
3108
|
}
|
|
2835
3109
|
renderLocalizationTab() {
|
|
3110
|
+
var _b;
|
|
3111
|
+
if (!this.revisionsWindowHidden)
|
|
3112
|
+
return '';
|
|
3113
|
+
if (((_b = this.definition) === null || _b === void 0 ? void 0 : _b.nodes.length) === 0)
|
|
3114
|
+
return '';
|
|
2836
3115
|
const languages = this.getLocalizationLanguages();
|
|
2837
3116
|
if (!languages.length) {
|
|
2838
3117
|
return html ``;
|
|
@@ -2843,7 +3122,7 @@ export class Editor extends RapidElement {
|
|
|
2843
3122
|
icon="language"
|
|
2844
3123
|
label="Translate Flow"
|
|
2845
3124
|
color="#6b7280"
|
|
2846
|
-
order="
|
|
3125
|
+
order="3"
|
|
2847
3126
|
.hidden=${!this.localizationWindowHidden}
|
|
2848
3127
|
@temba-button-clicked=${this.handleLocalizationTabClick}
|
|
2849
3128
|
></temba-floating-tab>
|
|
@@ -2890,23 +3169,44 @@ export class Editor extends RapidElement {
|
|
|
2890
3169
|
${unsafeCSS(CanvasNode.styles.cssText)}
|
|
2891
3170
|
</style>`;
|
|
2892
3171
|
const stickies = ((_c = (_b = this.definition) === null || _b === void 0 ? void 0 : _b._ui) === null || _c === void 0 ? void 0 : _c.stickies) || {};
|
|
2893
|
-
return html `${style} ${this.
|
|
2894
|
-
${this.
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
.
|
|
2901
|
-
|
|
3172
|
+
return html `${style} ${this.renderIssuesWindow()}
|
|
3173
|
+
${this.renderRevisionsWindow()} ${this.renderLocalizationWindow()}
|
|
3174
|
+
${this.renderAutoTranslateDialog()}
|
|
3175
|
+
<div id="editor-container">
|
|
3176
|
+
<div id="editor">
|
|
3177
|
+
${this.definition &&
|
|
3178
|
+
this.definition.nodes.length === 0 &&
|
|
3179
|
+
!this.isReadOnly()
|
|
3180
|
+
? html `<div class="empty-flow">
|
|
3181
|
+
<div class="empty-flow-content">
|
|
3182
|
+
<div class="empty-flow-title">This flow is empty</div>
|
|
3183
|
+
<div class="empty-flow-description">
|
|
3184
|
+
Get started by adding your first action or split to define
|
|
3185
|
+
how this flow will work.
|
|
3186
|
+
</div>
|
|
3187
|
+
<button
|
|
3188
|
+
class="empty-flow-button"
|
|
3189
|
+
@click=${this.handleEmptyFlowClick}
|
|
3190
|
+
>
|
|
3191
|
+
Add first step
|
|
3192
|
+
</button>
|
|
3193
|
+
</div>
|
|
3194
|
+
</div>`
|
|
3195
|
+
: ''}
|
|
2902
3196
|
<div
|
|
2903
|
-
id="
|
|
2904
|
-
class="${
|
|
3197
|
+
id="grid"
|
|
3198
|
+
class="${this.viewingRevision ? 'viewing-revision' : ''}"
|
|
3199
|
+
style="min-width:100%;width:${this.canvasSize
|
|
3200
|
+
.width}px; height:${this.canvasSize.height}px"
|
|
3201
|
+
>
|
|
3202
|
+
<div
|
|
3203
|
+
id="canvas"
|
|
3204
|
+
class="${getClasses({
|
|
2905
3205
|
'viewing-revision': !!this.viewingRevision,
|
|
2906
3206
|
'read-only-connections': !!this.viewingRevision || this.isTranslating
|
|
2907
3207
|
})}"
|
|
2908
|
-
|
|
2909
|
-
|
|
3208
|
+
>
|
|
3209
|
+
${this.definition
|
|
2910
3210
|
? repeat([...this.definition.nodes].sort((a, b) => a.uuid.localeCompare(b.uuid)), (node) => node.uuid, (node) => {
|
|
2911
3211
|
var _b, _c, _d;
|
|
2912
3212
|
const position = ((_c = (_b = this.definition._ui) === null || _b === void 0 ? void 0 : _b.nodes[node.uuid]) === null || _c === void 0 ? void 0 : _c.position) || {
|
|
@@ -2920,43 +3220,49 @@ export class Editor extends RapidElement {
|
|
|
2920
3220
|
const isFlowStart = this.definition.nodes.length > 0 &&
|
|
2921
3221
|
this.definition.nodes[0].uuid === node.uuid;
|
|
2922
3222
|
return html `<temba-flow-node
|
|
2923
|
-
|
|
2924
|
-
? '
|
|
2925
|
-
: ''} ${
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
3223
|
+
class="draggable ${dragging
|
|
3224
|
+
? 'dragging'
|
|
3225
|
+
: ''} ${selected ? 'selected' : ''} ${isFlowStart
|
|
3226
|
+
? 'flow-start'
|
|
3227
|
+
: ''}"
|
|
3228
|
+
@mousedown=${this.handleMouseDown.bind(this)}
|
|
3229
|
+
uuid=${node.uuid}
|
|
3230
|
+
data-node-uuid=${node.uuid}
|
|
3231
|
+
style="left:${position.left}px; top:${position.top}px;transition: all 0.2s ease-in-out;"
|
|
3232
|
+
.plumber=${this.plumber}
|
|
3233
|
+
.node=${node}
|
|
3234
|
+
.ui=${this.definition._ui.nodes[node.uuid]}
|
|
3235
|
+
@temba-node-deleted=${(event) => {
|
|
2934
3236
|
this.deleteNodes([event.detail.uuid]);
|
|
2935
3237
|
}}
|
|
2936
|
-
|
|
3238
|
+
></temba-flow-node>`;
|
|
2937
3239
|
})
|
|
2938
3240
|
: html `<temba-loading></temba-loading>`}
|
|
2939
|
-
|
|
3241
|
+
${repeat(Object.entries(stickies), ([uuid]) => uuid, ([uuid, sticky]) => {
|
|
2940
3242
|
var _b;
|
|
2941
3243
|
const position = sticky.position || { left: 0, top: 0 };
|
|
2942
3244
|
const dragging = this.isDragging && ((_b = this.currentDragItem) === null || _b === void 0 ? void 0 : _b.uuid) === uuid;
|
|
2943
3245
|
const selected = this.selectedItems.has(uuid);
|
|
2944
3246
|
return html `<temba-sticky-note
|
|
2945
|
-
|
|
3247
|
+
class="draggable ${dragging ? 'dragging' : ''} ${selected
|
|
2946
3248
|
? 'selected'
|
|
2947
3249
|
: ''}"
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
3250
|
+
@mousedown=${this.handleMouseDown.bind(this)}
|
|
3251
|
+
style="left:${position.left}px; top:${position.top}px;"
|
|
3252
|
+
uuid=${uuid}
|
|
3253
|
+
.data=${sticky}
|
|
3254
|
+
.dragging=${dragging}
|
|
3255
|
+
.selected=${selected}
|
|
3256
|
+
></temba-sticky-note>`;
|
|
2955
3257
|
})}
|
|
2956
|
-
|
|
2957
|
-
|
|
3258
|
+
${this.renderSelectionBox()} ${this.renderCanvasDropPreview()}
|
|
3259
|
+
${this.renderConnectionPlaceholder()}
|
|
3260
|
+
</div>
|
|
2958
3261
|
</div>
|
|
2959
3262
|
</div>
|
|
3263
|
+
<div class="save-indicator ${this.isSaving ? 'visible' : ''}">
|
|
3264
|
+
<temba-loading units="3" size="8"></temba-loading>
|
|
3265
|
+
</div>
|
|
2960
3266
|
</div>
|
|
2961
3267
|
|
|
2962
3268
|
${this.editingNode || this.editingAction
|
|
@@ -2964,6 +3270,7 @@ export class Editor extends RapidElement {
|
|
|
2964
3270
|
.node=${this.editingNode}
|
|
2965
3271
|
.nodeUI=${this.editingNodeUI}
|
|
2966
3272
|
.action=${this.editingAction}
|
|
3273
|
+
.dialogOrigin=${this.dialogOrigin}
|
|
2967
3274
|
@temba-node-saved=${(e) => this.handleNodeSaved(e.detail.node, e.detail.uiConfig)}
|
|
2968
3275
|
@temba-action-saved=${(e) => this.handleActionSaved(e.detail.action)}
|
|
2969
3276
|
@temba-node-edit-cancelled=${this.handleNodeEditCanceled}
|
|
@@ -2977,7 +3284,8 @@ export class Editor extends RapidElement {
|
|
|
2977
3284
|
.features=${this.features}
|
|
2978
3285
|
></temba-node-type-selector>`
|
|
2979
3286
|
: ''}
|
|
2980
|
-
${this.
|
|
3287
|
+
${this.renderIssuesTab()} ${this.renderRevisionsTab()}
|
|
3288
|
+
${this.renderLocalizationTab()} `;
|
|
2981
3289
|
}
|
|
2982
3290
|
}
|
|
2983
3291
|
__decorate([
|
|
@@ -3016,6 +3324,9 @@ __decorate([
|
|
|
3016
3324
|
__decorate([
|
|
3017
3325
|
fromStore(zustand, (state) => state.getCurrentActivity())
|
|
3018
3326
|
], Editor.prototype, "activityData", void 0);
|
|
3327
|
+
__decorate([
|
|
3328
|
+
fromStore(zustand, (state) => { var _b; return ((_b = state.flowInfo) === null || _b === void 0 ? void 0 : _b.issues) || []; })
|
|
3329
|
+
], Editor.prototype, "flowIssues", void 0);
|
|
3019
3330
|
__decorate([
|
|
3020
3331
|
state()
|
|
3021
3332
|
], Editor.prototype, "isDragging", void 0);
|
|
@@ -3046,6 +3357,9 @@ __decorate([
|
|
|
3046
3357
|
__decorate([
|
|
3047
3358
|
state()
|
|
3048
3359
|
], Editor.prototype, "isValidTarget", void 0);
|
|
3360
|
+
__decorate([
|
|
3361
|
+
state()
|
|
3362
|
+
], Editor.prototype, "issuesWindowHidden", void 0);
|
|
3049
3363
|
__decorate([
|
|
3050
3364
|
state()
|
|
3051
3365
|
], Editor.prototype, "localizationWindowHidden", void 0);
|
|
@@ -3079,6 +3393,12 @@ __decorate([
|
|
|
3079
3393
|
__decorate([
|
|
3080
3394
|
state()
|
|
3081
3395
|
], Editor.prototype, "isLoadingRevisions", void 0);
|
|
3396
|
+
__decorate([
|
|
3397
|
+
state()
|
|
3398
|
+
], Editor.prototype, "isSaving", void 0);
|
|
3399
|
+
__decorate([
|
|
3400
|
+
state()
|
|
3401
|
+
], Editor.prototype, "saveError", void 0);
|
|
3082
3402
|
__decorate([
|
|
3083
3403
|
state()
|
|
3084
3404
|
], Editor.prototype, "editingNode", void 0);
|