@nyaruka/temba-components 0.131.1 → 0.131.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +4 -1
- package/CHANGELOG.md +61 -1
- package/demo/data/flows/food-order.json +2 -2
- package/demo/data/flows/sample-flow.json +74 -125
- package/dist/static/svg/index.svg +1 -1
- package/dist/temba-components.js +1155 -618
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/Icons.js +4 -1
- package/out-tsc/src/Icons.js.map +1 -1
- package/out-tsc/src/events.js.map +1 -1
- package/out-tsc/src/flow/CanvasMenu.js +200 -0
- package/out-tsc/src/flow/CanvasMenu.js.map +1 -0
- package/out-tsc/src/flow/CanvasNode.js +327 -19
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +562 -66
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +240 -93
- package/out-tsc/src/flow/NodeEditor.js.map +1 -1
- package/out-tsc/src/flow/NodeTypeSelector.js +499 -0
- package/out-tsc/src/flow/NodeTypeSelector.js.map +1 -0
- package/out-tsc/src/flow/actions/add_contact_groups.js +3 -3
- package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
- package/out-tsc/src/flow/actions/add_contact_urn.js +62 -4
- package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
- package/out-tsc/src/flow/actions/add_input_labels.js +3 -3
- package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
- package/out-tsc/src/flow/actions/play_audio.js +2 -2
- package/out-tsc/src/flow/actions/play_audio.js.map +1 -1
- package/out-tsc/src/flow/actions/remove_contact_groups.js +6 -5
- package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -1
- package/out-tsc/src/flow/actions/request_optin.js +2 -2
- package/out-tsc/src/flow/actions/request_optin.js.map +1 -1
- package/out-tsc/src/flow/actions/say_msg.js +2 -2
- package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
- package/out-tsc/src/flow/actions/send_broadcast.js +76 -23
- package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
- package/out-tsc/src/flow/actions/send_email.js +4 -5
- package/out-tsc/src/flow/actions/send_email.js.map +1 -1
- package/out-tsc/src/flow/actions/send_msg.js +9 -19
- package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_channel.js +5 -9
- package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_field.js +19 -20
- package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_language.js +2 -2
- package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_name.js +2 -12
- package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_status.js +2 -2
- package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
- package/out-tsc/src/flow/actions/set_run_result.js +3 -3
- package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
- package/out-tsc/src/flow/actions/start_session.js +180 -6
- package/out-tsc/src/flow/actions/start_session.js.map +1 -1
- package/out-tsc/src/flow/config.js +11 -15
- package/out-tsc/src/flow/config.js.map +1 -1
- package/out-tsc/src/flow/currencies.js +45 -0
- package/out-tsc/src/flow/currencies.js.map +1 -0
- package/out-tsc/src/flow/nodes/shared-rules.js +257 -0
- package/out-tsc/src/flow/nodes/shared-rules.js.map +1 -0
- package/out-tsc/src/flow/nodes/shared.js +17 -0
- package/out-tsc/src/flow/nodes/shared.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_airtime.js +205 -5
- package/out-tsc/src/flow/nodes/split_by_airtime.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_contact_field.js +147 -3
- package/out-tsc/src/flow/nodes/split_by_contact_field.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_expression.js +68 -2
- package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_groups.js +12 -9
- package/out-tsc/src/flow/nodes/split_by_groups.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_intent.js +7 -0
- package/out-tsc/src/flow/nodes/split_by_intent.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_llm.js +3 -2
- package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +2 -2
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_random.js +3 -3
- package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_resthook.js +108 -0
- package/out-tsc/src/flow/nodes/split_by_resthook.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_run_result.js +206 -3
- package/out-tsc/src/flow/nodes/split_by_run_result.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_scheme.js +153 -2
- package/out-tsc/src/flow/nodes/split_by_scheme.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_subflow.js +6 -4
- package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_ticket.js +3 -2
- package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_webhook.js +3 -2
- package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_audio.js +2 -2
- package/out-tsc/src/flow/nodes/wait_for_audio.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_digits.js +2 -2
- package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_image.js +2 -2
- package/out-tsc/src/flow/nodes/wait_for_image.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_location.js +2 -2
- package/out-tsc/src/flow/nodes/wait_for_location.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_menu.js +2 -2
- package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js +32 -567
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_video.js +2 -2
- package/out-tsc/src/flow/nodes/wait_for_video.js.map +1 -1
- package/out-tsc/src/flow/types.js +71 -12
- package/out-tsc/src/flow/types.js.map +1 -1
- package/out-tsc/src/flow/utils.js +101 -14
- package/out-tsc/src/flow/utils.js.map +1 -1
- package/out-tsc/src/form/FieldRenderer.js +2 -4
- package/out-tsc/src/form/FieldRenderer.js.map +1 -1
- package/out-tsc/src/interfaces.js +3 -0
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/src/list/SortableList.js +98 -33
- package/out-tsc/src/list/SortableList.js.map +1 -1
- package/out-tsc/src/live/ContactChat.js +15 -18
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/src/store/AppState.js +53 -0
- package/out-tsc/src/store/AppState.js.map +1 -1
- package/out-tsc/src/utils.js +254 -13
- package/out-tsc/src/utils.js.map +1 -1
- package/out-tsc/temba-modules.js +4 -0
- package/out-tsc/temba-modules.js.map +1 -1
- package/out-tsc/test/ActionHelper.js +3 -3
- package/out-tsc/test/ActionHelper.js.map +1 -1
- package/out-tsc/test/NodeHelper.js +6 -3
- package/out-tsc/test/NodeHelper.js.map +1 -1
- package/out-tsc/test/actions/add_contact_urn.test.js +202 -0
- package/out-tsc/test/actions/add_contact_urn.test.js.map +1 -0
- package/out-tsc/test/actions/send_broadcast.test.js +148 -0
- package/out-tsc/test/actions/send_broadcast.test.js.map +1 -0
- package/out-tsc/test/actions/send_email.test.js +17 -23
- package/out-tsc/test/actions/send_email.test.js.map +1 -1
- package/out-tsc/test/actions/send_msg.test.js +33 -15
- package/out-tsc/test/actions/send_msg.test.js.map +1 -1
- package/out-tsc/test/actions/start_session.test.js +116 -0
- package/out-tsc/test/actions/start_session.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_airtime.test.js +604 -0
- package/out-tsc/test/nodes/split_by_airtime.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_contact_field.test.js +387 -0
- package/out-tsc/test/nodes/split_by_contact_field.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_expression.test.js +614 -0
- package/out-tsc/test/nodes/split_by_expression.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_random.test.js +3 -3
- package/out-tsc/test/nodes/split_by_random.test.js.map +1 -1
- package/out-tsc/test/nodes/split_by_resthook.test.js +337 -0
- package/out-tsc/test/nodes/split_by_resthook.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_run_result.test.js +920 -0
- package/out-tsc/test/nodes/split_by_run_result.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_scheme.test.js +399 -0
- package/out-tsc/test/nodes/split_by_scheme.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_subflow.test.js +333 -0
- package/out-tsc/test/nodes/split_by_subflow.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_digits.test.js +2 -2
- package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -1
- package/out-tsc/test/nodes/wait_for_response.test.js +2 -1
- package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -1
- package/out-tsc/test/temba-action-drag-between-nodes.test.js +252 -0
- package/out-tsc/test/temba-action-drag-between-nodes.test.js.map +1 -0
- package/out-tsc/test/temba-canvas-menu.test.js +122 -0
- package/out-tsc/test/temba-canvas-menu.test.js.map +1 -0
- package/out-tsc/test/temba-flow-editor-node.test.js +85 -2
- package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
- package/out-tsc/test/temba-flow-editor.test.js +7 -8
- package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
- package/out-tsc/test/temba-node-editor.test.js +3 -1
- package/out-tsc/test/temba-node-editor.test.js.map +1 -1
- package/out-tsc/test/temba-node-type-selector.test.js +115 -0
- package/out-tsc/test/temba-node-type-selector.test.js.map +1 -0
- package/out-tsc/test/temba-omnibox.test.js +2 -1
- package/out-tsc/test/temba-omnibox.test.js.map +1 -1
- package/out-tsc/test/temba-sortable-list.test.js +51 -0
- package/out-tsc/test/temba-sortable-list.test.js.map +1 -1
- package/out-tsc/test/temba-utils-index.test.js +1 -27
- package/out-tsc/test/temba-utils-index.test.js.map +1 -1
- package/out-tsc/test/utils.test.js +2 -0
- package/out-tsc/test/utils.test.js.map +1 -1
- package/package.json +2 -1
- package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/multiple-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/single-group.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/expression-facebook.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/expression-phone.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/facebook-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/instagram-handle.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/line-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/phone-number.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/telegram-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/viber-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/wechat-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/whatsapp.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/expression-facebook.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/expression-phone.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/facebook-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/instagram-handle.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/line-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/phone-number.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/telegram-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/viber-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/wechat-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/whatsapp.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/contacts-only.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/groups-only.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/many-groups.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/multiline-text.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/with-attachments.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/contacts-only.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/groups-only.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/many-groups.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/multiline-text.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/with-attachments.png +0 -0
- package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
- package/screenshots/truth/actions/send_email/editor/empty-body.png +0 -0
- package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
- package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
- package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
- package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
- package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
- package/screenshots/truth/actions/send_email/editor/with-expressions.png +0 -0
- package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
- package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
- package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
- package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
- package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
- package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
- package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
- package/screenshots/truth/actions/send_email/render/with-expressions.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-without-quick-replies.png +0 -0
- package/screenshots/truth/actions/start_session/editor/contact-query.png +0 -0
- package/screenshots/truth/actions/start_session/editor/contacts-only.png +0 -0
- package/screenshots/truth/actions/start_session/editor/create-contact.png +0 -0
- package/screenshots/truth/actions/start_session/editor/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/start_session/editor/groups-only.png +0 -0
- package/screenshots/truth/actions/start_session/editor/many-recipients.png +0 -0
- package/screenshots/truth/actions/start_session/render/contact-query.png +0 -0
- package/screenshots/truth/actions/start_session/render/contacts-only.png +0 -0
- package/screenshots/truth/actions/start_session/render/create-contact.png +0 -0
- package/screenshots/truth/actions/start_session/render/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/start_session/render/groups-only.png +0 -0
- package/screenshots/truth/actions/start_session/render/many-recipients.png +0 -0
- package/screenshots/truth/canvas-menu/open.png +0 -0
- package/screenshots/truth/editor/router.png +0 -0
- package/screenshots/truth/editor/wait.png +0 -0
- package/screenshots/truth/list/fields-dragging.png +0 -0
- package/screenshots/truth/list/sortable-dragging.png +0 -0
- package/screenshots/truth/node-type-selector/action-mode.png +0 -0
- package/screenshots/truth/node-type-selector/split-mode.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/editor/information-extraction.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/editor/sentiment-analysis.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/editor/summarization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/editor/translation-task.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/summarization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/translation-task.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/basic-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/custom-input-and-result-name.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/many-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/minimal-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/ab-test-multiple-variants.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/sampling-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/three-way-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/two-bucket-split.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
- package/src/Icons.ts +4 -1
- package/src/events.ts +2 -6
- package/src/flow/CanvasMenu.ts +217 -0
- package/src/flow/CanvasNode.ts +408 -10
- package/src/flow/Editor.ts +683 -44
- package/src/flow/NodeEditor.ts +304 -125
- package/src/flow/NodeTypeSelector.ts +592 -0
- package/src/flow/actions/add_contact_groups.ts +4 -4
- package/src/flow/actions/add_contact_urn.ts +76 -4
- package/src/flow/actions/add_input_labels.ts +4 -4
- package/src/flow/actions/play_audio.ts +2 -2
- package/src/flow/actions/remove_contact_groups.ts +14 -6
- package/src/flow/actions/request_optin.ts +2 -2
- package/src/flow/actions/say_msg.ts +2 -2
- package/src/flow/actions/send_broadcast.ts +85 -23
- package/src/flow/actions/send_email.ts +10 -6
- package/src/flow/actions/send_msg.ts +22 -32
- package/src/flow/actions/set_contact_channel.ts +5 -11
- package/src/flow/actions/set_contact_field.ts +20 -25
- package/src/flow/actions/set_contact_language.ts +9 -4
- package/src/flow/actions/set_contact_name.ts +3 -15
- package/src/flow/actions/set_contact_status.ts +3 -3
- package/src/flow/actions/set_run_result.ts +4 -4
- package/src/flow/actions/start_session.ts +208 -6
- package/src/flow/config.ts +13 -15
- package/src/flow/currencies.ts +51 -0
- package/src/flow/nodes/shared-rules.ts +301 -0
- package/src/flow/nodes/shared.ts +18 -0
- package/src/flow/nodes/split_by_airtime.ts +238 -5
- package/src/flow/nodes/split_by_contact_field.ts +185 -3
- package/src/flow/nodes/split_by_expression.ts +94 -2
- package/src/flow/nodes/split_by_groups.ts +15 -10
- package/src/flow/nodes/split_by_intent.ts +7 -0
- package/src/flow/nodes/split_by_llm.ts +4 -3
- package/src/flow/nodes/split_by_llm_categorize.ts +4 -4
- package/src/flow/nodes/split_by_random.ts +5 -5
- package/src/flow/nodes/split_by_resthook.ts +130 -0
- package/src/flow/nodes/split_by_run_result.ts +249 -3
- package/src/flow/nodes/split_by_scheme.ts +192 -2
- package/src/flow/nodes/split_by_subflow.ts +6 -4
- package/src/flow/nodes/split_by_ticket.ts +4 -3
- package/src/flow/nodes/split_by_webhook.ts +6 -5
- package/src/flow/nodes/wait_for_audio.ts +2 -2
- package/src/flow/nodes/wait_for_digits.ts +2 -2
- package/src/flow/nodes/wait_for_image.ts +2 -2
- package/src/flow/nodes/wait_for_location.ts +2 -2
- package/src/flow/nodes/wait_for_menu.ts +2 -2
- package/src/flow/nodes/wait_for_response.ts +48 -679
- package/src/flow/nodes/wait_for_video.ts +2 -2
- package/src/flow/types.ts +109 -23
- package/src/flow/utils.ts +108 -14
- package/src/form/FieldRenderer.ts +2 -4
- package/src/interfaces.ts +3 -0
- package/src/list/SortableList.ts +109 -34
- package/src/live/ContactChat.ts +15 -18
- package/src/store/AppState.ts +69 -0
- package/src/store/flow-definition.d.ts +2 -5
- package/src/utils.ts +332 -12
- package/static/api/channels.json +46 -0
- package/static/api/resthooks.json +31 -0
- package/static/svg/index.svg +1 -1
- package/static/svg/work/traced/lightning-02.svg +1 -0
- package/static/svg/work/used/lightning-02.svg +3 -0
- package/temba-modules.ts +4 -0
- package/test/ActionHelper.ts +3 -3
- package/test/NodeHelper.ts +6 -3
- package/test/actions/add_contact_urn.test.ts +287 -0
- package/test/actions/send_broadcast.test.ts +190 -0
- package/test/actions/send_email.test.ts +17 -23
- package/test/actions/send_msg.test.ts +39 -15
- package/test/actions/start_session.test.ts +151 -0
- package/test/nodes/split_by_airtime.test.ts +673 -0
- package/test/nodes/split_by_contact_field.test.ts +451 -0
- package/test/nodes/split_by_expression.test.ts +751 -0
- package/test/nodes/split_by_random.test.ts +3 -3
- package/test/nodes/split_by_resthook.test.ts +398 -0
- package/test/nodes/split_by_run_result.test.ts +1109 -0
- package/test/nodes/split_by_scheme.test.ts +486 -0
- package/test/nodes/split_by_subflow.test.ts +381 -0
- package/test/nodes/wait_for_digits.test.ts +2 -2
- package/test/nodes/wait_for_response.test.ts +2 -1
- package/test/temba-action-drag-between-nodes.test.ts +301 -0
- package/test/temba-canvas-menu.test.ts +156 -0
- package/test/temba-flow-editor-node.test.ts +102 -2
- package/test/temba-flow-editor.test.ts +7 -8
- package/test/temba-node-editor.test.ts +3 -1
- package/test/temba-node-type-selector.test.ts +152 -0
- package/test/temba-omnibox.test.ts +2 -1
- package/test/temba-sortable-list.test.ts +69 -0
- package/test/temba-utils-index.test.ts +0 -35
- package/test/utils.test.ts +2 -0
- package/test-assets/contacts/history.json +14 -20
- package/web-dev-server.config.mjs +3 -1
- package/out-tsc/src/flow/actions/call_classifier.js +0 -11
- package/out-tsc/src/flow/actions/call_classifier.js.map +0 -1
- package/out-tsc/src/flow/actions/call_resthook.js +0 -11
- package/out-tsc/src/flow/actions/call_resthook.js.map +0 -1
- package/out-tsc/src/flow/actions/split_by_expression_example.js +0 -77
- package/out-tsc/src/flow/actions/split_by_expression_example.js.map +0 -1
- package/out-tsc/src/flow/actions/transfer_airtime.js +0 -11
- package/out-tsc/src/flow/actions/transfer_airtime.js.map +0 -1
- package/src/flow/actions/call_classifier.ts +0 -12
- package/src/flow/actions/call_resthook.ts +0 -12
- package/src/flow/actions/split_by_expression_example.ts +0 -88
- package/src/flow/actions/transfer_airtime.ts +0 -12
package/src/flow/NodeEditor.ts
CHANGED
|
@@ -10,7 +10,14 @@ import {
|
|
|
10
10
|
FieldConfig,
|
|
11
11
|
ActionConfig
|
|
12
12
|
} from './config';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
LayoutItem,
|
|
15
|
+
RowLayoutConfig,
|
|
16
|
+
GroupLayoutConfig,
|
|
17
|
+
FormData,
|
|
18
|
+
ACTION_GROUP_METADATA,
|
|
19
|
+
SPLIT_GROUP_METADATA
|
|
20
|
+
} from './types';
|
|
14
21
|
import { CustomEventType } from '../interfaces';
|
|
15
22
|
import { generateUUID } from '../utils';
|
|
16
23
|
import { FieldRenderer } from '../form/FieldRenderer';
|
|
@@ -25,7 +32,6 @@ export class NodeEditor extends RapidElement {
|
|
|
25
32
|
flex-direction: column;
|
|
26
33
|
gap: 15px;
|
|
27
34
|
min-width: 400px;
|
|
28
|
-
padding-bottom: 40px;
|
|
29
35
|
|
|
30
36
|
--color-bubble-bg: rgba(var(--primary-rgb), 0.7);
|
|
31
37
|
--color-bubble-border: rgba(0, 0, 0, 0.2);
|
|
@@ -88,6 +94,28 @@ export class NodeEditor extends RapidElement {
|
|
|
88
94
|
align-items: center;
|
|
89
95
|
}
|
|
90
96
|
|
|
97
|
+
.form-row-wrapper {
|
|
98
|
+
display: flex;
|
|
99
|
+
flex-direction: column;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.form-row-label {
|
|
103
|
+
margin-bottom: 5px;
|
|
104
|
+
margin-left: 4px;
|
|
105
|
+
display: block;
|
|
106
|
+
font-weight: 400;
|
|
107
|
+
font-size: var(--label-size);
|
|
108
|
+
letter-spacing: 0.05em;
|
|
109
|
+
line-height: normal;
|
|
110
|
+
color: var(--color-label, #777);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.form-row-help {
|
|
114
|
+
font-size: 12px;
|
|
115
|
+
color: #666;
|
|
116
|
+
margin-top: 6px;
|
|
117
|
+
}
|
|
118
|
+
|
|
91
119
|
.form-group {
|
|
92
120
|
border: 1px solid #e0e0e0;
|
|
93
121
|
border-radius: 6px;
|
|
@@ -276,6 +304,21 @@ export class NodeEditor extends RapidElement {
|
|
|
276
304
|
.gutter-fields temba-select {
|
|
277
305
|
min-width: 120px;
|
|
278
306
|
}
|
|
307
|
+
|
|
308
|
+
.optional-field-link {
|
|
309
|
+
margin: 10px 0;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.optional-field-link a {
|
|
313
|
+
color: var(--color-link-primary, #0066cc);
|
|
314
|
+
text-decoration: none;
|
|
315
|
+
font-size: 13px;
|
|
316
|
+
cursor: pointer;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.optional-field-link a:hover {
|
|
320
|
+
text-decoration: underline;
|
|
321
|
+
}
|
|
279
322
|
`;
|
|
280
323
|
}
|
|
281
324
|
|
|
@@ -292,10 +335,10 @@ export class NodeEditor extends RapidElement {
|
|
|
292
335
|
isOpen: boolean = false;
|
|
293
336
|
|
|
294
337
|
@state()
|
|
295
|
-
private formData:
|
|
338
|
+
private formData: FormData = {};
|
|
296
339
|
|
|
297
340
|
@state()
|
|
298
|
-
private originalFormData:
|
|
341
|
+
private originalFormData: FormData = {};
|
|
299
342
|
|
|
300
343
|
@state()
|
|
301
344
|
private errors: { [key: string]: string } = {};
|
|
@@ -306,6 +349,9 @@ export class NodeEditor extends RapidElement {
|
|
|
306
349
|
@state()
|
|
307
350
|
private groupHoverState: { [key: string]: boolean } = {};
|
|
308
351
|
|
|
352
|
+
@state()
|
|
353
|
+
private revealedOptionalFields: Set<string> = new Set();
|
|
354
|
+
|
|
309
355
|
connectedCallback(): void {
|
|
310
356
|
super.connectedCallback();
|
|
311
357
|
this.initializeFormData();
|
|
@@ -361,7 +407,7 @@ export class NodeEditor extends RapidElement {
|
|
|
361
407
|
// Node editing mode - use node config
|
|
362
408
|
const nodeConfig = this.getNodeConfig();
|
|
363
409
|
if (nodeConfig?.toFormData) {
|
|
364
|
-
this.formData = nodeConfig.toFormData(this.node);
|
|
410
|
+
this.formData = nodeConfig.toFormData(this.node, this.nodeUI);
|
|
365
411
|
} else {
|
|
366
412
|
this.formData = { ...this.node };
|
|
367
413
|
}
|
|
@@ -488,7 +534,10 @@ export class NodeEditor extends RapidElement {
|
|
|
488
534
|
|
|
489
535
|
private getHeaderColor(): string {
|
|
490
536
|
const config = this.getConfig();
|
|
491
|
-
return config?.
|
|
537
|
+
return config?.group
|
|
538
|
+
? ACTION_GROUP_METADATA[config.group]?.color ||
|
|
539
|
+
SPLIT_GROUP_METADATA[config.group]?.color
|
|
540
|
+
: '#aaaaaa';
|
|
492
541
|
}
|
|
493
542
|
|
|
494
543
|
private handleDialogButtonClick(event: CustomEvent): void {
|
|
@@ -522,8 +571,16 @@ export class NodeEditor extends RapidElement {
|
|
|
522
571
|
if (this.node && this.node.router) {
|
|
523
572
|
// Node editing mode with router - use formDataToNode
|
|
524
573
|
const updatedNode = this.formDataToNode(processedFormData);
|
|
574
|
+
|
|
575
|
+
// Generate UI config if the node config provides a toUIConfig function
|
|
576
|
+
const nodeConfig = this.getNodeConfig();
|
|
577
|
+
const uiConfig = nodeConfig?.toUIConfig
|
|
578
|
+
? nodeConfig.toUIConfig(processedFormData)
|
|
579
|
+
: undefined;
|
|
580
|
+
|
|
525
581
|
this.fireCustomEvent(CustomEventType.NodeSaved, {
|
|
526
|
-
node: updatedNode
|
|
582
|
+
node: updatedNode,
|
|
583
|
+
uiConfig
|
|
527
584
|
});
|
|
528
585
|
} else if (this.action) {
|
|
529
586
|
// Pure action editing mode (no router)
|
|
@@ -534,13 +591,21 @@ export class NodeEditor extends RapidElement {
|
|
|
534
591
|
} else if (this.node) {
|
|
535
592
|
// Node editing mode without router
|
|
536
593
|
const updatedNode = this.formDataToNode(processedFormData);
|
|
594
|
+
|
|
595
|
+
// Generate UI config if the node config provides a toUIConfig function
|
|
596
|
+
const nodeConfig = this.getNodeConfig();
|
|
597
|
+
const uiConfig = nodeConfig?.toUIConfig
|
|
598
|
+
? nodeConfig.toUIConfig(processedFormData)
|
|
599
|
+
: undefined;
|
|
600
|
+
|
|
537
601
|
this.fireCustomEvent(CustomEventType.NodeSaved, {
|
|
538
|
-
node: updatedNode
|
|
602
|
+
node: updatedNode,
|
|
603
|
+
uiConfig
|
|
539
604
|
});
|
|
540
605
|
}
|
|
541
606
|
}
|
|
542
607
|
|
|
543
|
-
private processFormDataForSave():
|
|
608
|
+
private processFormDataForSave(): FormData {
|
|
544
609
|
const processed = { ...this.formData };
|
|
545
610
|
|
|
546
611
|
// Convert key-value arrays to Records
|
|
@@ -743,12 +808,21 @@ export class NodeEditor extends RapidElement {
|
|
|
743
808
|
});
|
|
744
809
|
}
|
|
745
810
|
|
|
746
|
-
private formDataToNode(formData:
|
|
811
|
+
private formDataToNode(formData: FormData = this.formData): Node {
|
|
747
812
|
if (!this.node) throw new Error('No node to update');
|
|
748
813
|
let updatedNode: Node = { ...this.node };
|
|
749
814
|
|
|
815
|
+
// Check if node config has fromFormData - if so, it handles the entire transformation
|
|
816
|
+
const nodeConfig = this.getNodeConfig();
|
|
817
|
+
const nodeHasFromFormData = nodeConfig?.fromFormData !== undefined;
|
|
818
|
+
|
|
750
819
|
// Handle actions using action config transformations if available
|
|
751
|
-
if
|
|
820
|
+
// Skip this if the node has its own fromFormData (which handles actions itself)
|
|
821
|
+
if (
|
|
822
|
+
!nodeHasFromFormData &&
|
|
823
|
+
this.node.actions &&
|
|
824
|
+
this.node.actions.length > 0
|
|
825
|
+
) {
|
|
752
826
|
updatedNode.actions = this.node.actions.map((action) => {
|
|
753
827
|
// If we're editing a specific action, only transform that one
|
|
754
828
|
if (this.action && action.uuid === this.action.uuid) {
|
|
@@ -768,66 +842,105 @@ export class NodeEditor extends RapidElement {
|
|
|
768
842
|
}
|
|
769
843
|
|
|
770
844
|
// Handle router configuration using node config
|
|
771
|
-
if (
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
845
|
+
if (nodeHasFromFormData) {
|
|
846
|
+
// Use node-specific form data transformation
|
|
847
|
+
// When a node has fromFormData, it's responsible for creating the entire
|
|
848
|
+
// node structure including actions and router (regardless of whether router exists yet)
|
|
849
|
+
updatedNode = nodeConfig.fromFormData!(formData, updatedNode);
|
|
850
|
+
} else if (this.node.router) {
|
|
851
|
+
// Default router handling when no nodeConfig.fromFormData
|
|
852
|
+
updatedNode.router = { ...this.node.router };
|
|
853
|
+
|
|
854
|
+
// Apply form data to router fields if they exist
|
|
855
|
+
if (formData.result_name !== undefined) {
|
|
856
|
+
updatedNode.router.result_name = formData.result_name;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// Handle preconfigured rules from node config
|
|
860
|
+
if (nodeConfig?.router?.rules) {
|
|
861
|
+
// Build a complete new set of categories and exits based on node config
|
|
862
|
+
const existingCategories = updatedNode.router.categories || [];
|
|
863
|
+
const existingExits = updatedNode.exits || [];
|
|
864
|
+
|
|
865
|
+
const newCategories: any[] = [];
|
|
866
|
+
const newExits: any[] = [];
|
|
867
|
+
|
|
868
|
+
// Group rules by category name to handle multiple rules pointing to the same category
|
|
869
|
+
const categoryNameToRules = new Map<
|
|
870
|
+
string,
|
|
871
|
+
typeof nodeConfig.router.rules
|
|
872
|
+
>();
|
|
873
|
+
nodeConfig.router.rules.forEach((rule) => {
|
|
874
|
+
if (!categoryNameToRules.has(rule.categoryName)) {
|
|
875
|
+
categoryNameToRules.set(rule.categoryName, []);
|
|
876
|
+
}
|
|
877
|
+
categoryNameToRules.get(rule.categoryName)!.push(rule);
|
|
878
|
+
});
|
|
780
879
|
|
|
781
|
-
//
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
880
|
+
// Create categories for all unique category names
|
|
881
|
+
categoryNameToRules.forEach((rules, categoryName) => {
|
|
882
|
+
// Check if category already exists to preserve its UUID and exit_uuid
|
|
883
|
+
const existingCategory = existingCategories.find(
|
|
884
|
+
(cat) => cat.name === categoryName
|
|
885
|
+
);
|
|
785
886
|
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
// Group rules by category name to handle multiple rules pointing to the same category
|
|
796
|
-
const categoryNameToRules = new Map<
|
|
797
|
-
string,
|
|
798
|
-
typeof nodeConfig.router.rules
|
|
799
|
-
>();
|
|
800
|
-
nodeConfig.router.rules.forEach((rule) => {
|
|
801
|
-
if (!categoryNameToRules.has(rule.categoryName)) {
|
|
802
|
-
categoryNameToRules.set(rule.categoryName, []);
|
|
887
|
+
if (existingCategory) {
|
|
888
|
+
// Preserve existing category and its associated exit
|
|
889
|
+
newCategories.push(existingCategory);
|
|
890
|
+
const associatedExit = existingExits.find(
|
|
891
|
+
(exit) => exit.uuid === existingCategory.exit_uuid
|
|
892
|
+
);
|
|
893
|
+
if (associatedExit) {
|
|
894
|
+
newExits.push(associatedExit);
|
|
803
895
|
}
|
|
804
|
-
|
|
805
|
-
|
|
896
|
+
} else {
|
|
897
|
+
// Create new category and exit
|
|
898
|
+
const categoryUuid = generateUUID();
|
|
899
|
+
const exitUuid = generateUUID();
|
|
900
|
+
|
|
901
|
+
newCategories.push({
|
|
902
|
+
uuid: categoryUuid,
|
|
903
|
+
name: categoryName,
|
|
904
|
+
exit_uuid: exitUuid
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
newExits.push({
|
|
908
|
+
uuid: exitUuid,
|
|
909
|
+
destination_uuid: null
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
// Add default category if specified
|
|
915
|
+
if (nodeConfig.router.defaultCategory) {
|
|
916
|
+
// Check if default category already exists in our new list
|
|
917
|
+
const existingDefault = newCategories.find(
|
|
918
|
+
(cat) => cat.name === nodeConfig.router.defaultCategory
|
|
919
|
+
);
|
|
806
920
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
(cat) => cat.name === categoryName
|
|
921
|
+
if (!existingDefault) {
|
|
922
|
+
// Check if it exists in the original categories
|
|
923
|
+
const originalDefault = existingCategories.find(
|
|
924
|
+
(cat) => cat.name === nodeConfig.router.defaultCategory
|
|
812
925
|
);
|
|
813
926
|
|
|
814
|
-
if (
|
|
815
|
-
// Preserve existing category and its
|
|
816
|
-
newCategories.push(
|
|
927
|
+
if (originalDefault) {
|
|
928
|
+
// Preserve existing default category and its exit
|
|
929
|
+
newCategories.push(originalDefault);
|
|
817
930
|
const associatedExit = existingExits.find(
|
|
818
|
-
(exit) => exit.uuid ===
|
|
931
|
+
(exit) => exit.uuid === originalDefault.exit_uuid
|
|
819
932
|
);
|
|
820
933
|
if (associatedExit) {
|
|
821
934
|
newExits.push(associatedExit);
|
|
822
935
|
}
|
|
823
936
|
} else {
|
|
824
|
-
// Create new category and exit
|
|
937
|
+
// Create new default category and exit
|
|
825
938
|
const categoryUuid = generateUUID();
|
|
826
939
|
const exitUuid = generateUUID();
|
|
827
940
|
|
|
828
941
|
newCategories.push({
|
|
829
942
|
uuid: categoryUuid,
|
|
830
|
-
name:
|
|
943
|
+
name: nodeConfig.router.defaultCategory,
|
|
831
944
|
exit_uuid: exitUuid
|
|
832
945
|
});
|
|
833
946
|
|
|
@@ -836,53 +949,12 @@ export class NodeEditor extends RapidElement {
|
|
|
836
949
|
destination_uuid: null
|
|
837
950
|
});
|
|
838
951
|
}
|
|
839
|
-
});
|
|
840
|
-
|
|
841
|
-
// Add default category if specified
|
|
842
|
-
if (nodeConfig.router.defaultCategory) {
|
|
843
|
-
// Check if default category already exists in our new list
|
|
844
|
-
const existingDefault = newCategories.find(
|
|
845
|
-
(cat) => cat.name === nodeConfig.router.defaultCategory
|
|
846
|
-
);
|
|
847
|
-
|
|
848
|
-
if (!existingDefault) {
|
|
849
|
-
// Check if it exists in the original categories
|
|
850
|
-
const originalDefault = existingCategories.find(
|
|
851
|
-
(cat) => cat.name === nodeConfig.router.defaultCategory
|
|
852
|
-
);
|
|
853
|
-
|
|
854
|
-
if (originalDefault) {
|
|
855
|
-
// Preserve existing default category and its exit
|
|
856
|
-
newCategories.push(originalDefault);
|
|
857
|
-
const associatedExit = existingExits.find(
|
|
858
|
-
(exit) => exit.uuid === originalDefault.exit_uuid
|
|
859
|
-
);
|
|
860
|
-
if (associatedExit) {
|
|
861
|
-
newExits.push(associatedExit);
|
|
862
|
-
}
|
|
863
|
-
} else {
|
|
864
|
-
// Create new default category and exit
|
|
865
|
-
const categoryUuid = generateUUID();
|
|
866
|
-
const exitUuid = generateUUID();
|
|
867
|
-
|
|
868
|
-
newCategories.push({
|
|
869
|
-
uuid: categoryUuid,
|
|
870
|
-
name: nodeConfig.router.defaultCategory,
|
|
871
|
-
exit_uuid: exitUuid
|
|
872
|
-
});
|
|
873
|
-
|
|
874
|
-
newExits.push({
|
|
875
|
-
uuid: exitUuid,
|
|
876
|
-
destination_uuid: null
|
|
877
|
-
});
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
952
|
}
|
|
881
|
-
|
|
882
|
-
// Replace the entire categories and exits lists with our complete new sets
|
|
883
|
-
updatedNode.router.categories = newCategories;
|
|
884
|
-
updatedNode.exits = newExits;
|
|
885
953
|
}
|
|
954
|
+
|
|
955
|
+
// Replace the entire categories and exits lists with our complete new sets
|
|
956
|
+
updatedNode.router.categories = newCategories;
|
|
957
|
+
updatedNode.exits = newExits;
|
|
886
958
|
}
|
|
887
959
|
} else {
|
|
888
960
|
// If no router, just apply form data to node properties
|
|
@@ -901,7 +973,7 @@ export class NodeEditor extends RapidElement {
|
|
|
901
973
|
return updatedNode;
|
|
902
974
|
}
|
|
903
975
|
|
|
904
|
-
private formDataToAction(formData:
|
|
976
|
+
private formDataToAction(formData: FormData = this.formData): Action {
|
|
905
977
|
if (!this.action) throw new Error('No action to update');
|
|
906
978
|
|
|
907
979
|
// Use action config transformation if available
|
|
@@ -1009,23 +1081,31 @@ export class NodeEditor extends RapidElement {
|
|
|
1009
1081
|
});
|
|
1010
1082
|
}
|
|
1011
1083
|
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
): TemplateResult {
|
|
1017
|
-
// Check visibility condition
|
|
1084
|
+
/**
|
|
1085
|
+
* Helper method to check if a field is visible based on its conditions
|
|
1086
|
+
*/
|
|
1087
|
+
private isFieldVisible(fieldName: string, config: FieldConfig): boolean {
|
|
1018
1088
|
if (config.conditions?.visible) {
|
|
1019
1089
|
try {
|
|
1020
|
-
|
|
1021
|
-
if (!isVisible) {
|
|
1022
|
-
return html``;
|
|
1023
|
-
}
|
|
1090
|
+
return config.conditions.visible(this.formData);
|
|
1024
1091
|
} catch (error) {
|
|
1025
1092
|
console.error(`Error checking visibility for ${fieldName}:`, error);
|
|
1026
1093
|
// If there's an error, show the field by default
|
|
1094
|
+
return true;
|
|
1027
1095
|
}
|
|
1028
1096
|
}
|
|
1097
|
+
return true;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
private renderNewField(
|
|
1101
|
+
fieldName: string,
|
|
1102
|
+
config: FieldConfig,
|
|
1103
|
+
value: any
|
|
1104
|
+
): TemplateResult {
|
|
1105
|
+
// Check visibility condition
|
|
1106
|
+
if (!this.isFieldVisible(fieldName, config)) {
|
|
1107
|
+
return html``;
|
|
1108
|
+
}
|
|
1029
1109
|
|
|
1030
1110
|
const errors = this.errors[fieldName] ? [this.errors[fieldName]] : [];
|
|
1031
1111
|
|
|
@@ -1049,6 +1129,43 @@ export class NodeEditor extends RapidElement {
|
|
|
1049
1129
|
return fieldContent;
|
|
1050
1130
|
}
|
|
1051
1131
|
|
|
1132
|
+
private renderOptionalField(
|
|
1133
|
+
fieldName: string,
|
|
1134
|
+
config: FieldConfig,
|
|
1135
|
+
value: any
|
|
1136
|
+
): TemplateResult {
|
|
1137
|
+
// If the field has a value or has been revealed, show it
|
|
1138
|
+
const hasValue = value && value.toString().trim() !== '';
|
|
1139
|
+
const isRevealed = this.revealedOptionalFields.has(fieldName);
|
|
1140
|
+
|
|
1141
|
+
if (hasValue || isRevealed) {
|
|
1142
|
+
// Render the field normally
|
|
1143
|
+
return this.renderNewField(fieldName, config, value);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
// Show the "Save as..." link
|
|
1147
|
+
return html`
|
|
1148
|
+
<div class="optional-field-link">
|
|
1149
|
+
<a
|
|
1150
|
+
href="#"
|
|
1151
|
+
@click="${(e: Event) => {
|
|
1152
|
+
e.preventDefault();
|
|
1153
|
+
this.revealOptionalField(fieldName);
|
|
1154
|
+
}}"
|
|
1155
|
+
>
|
|
1156
|
+
${config.optionalLink}
|
|
1157
|
+
</a>
|
|
1158
|
+
</div>
|
|
1159
|
+
`;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
private revealOptionalField(fieldName: string): void {
|
|
1163
|
+
this.revealedOptionalFields = new Set([
|
|
1164
|
+
...this.revealedOptionalFields,
|
|
1165
|
+
fieldName
|
|
1166
|
+
]);
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1052
1169
|
private renderFieldContent(
|
|
1053
1170
|
fieldName: string,
|
|
1054
1171
|
config: FieldConfig,
|
|
@@ -1160,9 +1277,20 @@ export class NodeEditor extends RapidElement {
|
|
|
1160
1277
|
case 'field':
|
|
1161
1278
|
if (config.form![item.field] && !renderedFields.has(item.field)) {
|
|
1162
1279
|
renderedFields.add(item.field);
|
|
1280
|
+
const fieldConfig = config.form![item.field] as FieldConfig;
|
|
1281
|
+
|
|
1282
|
+
// Handle optional link fields
|
|
1283
|
+
if (fieldConfig.optionalLink) {
|
|
1284
|
+
return this.renderOptionalField(
|
|
1285
|
+
item.field,
|
|
1286
|
+
fieldConfig,
|
|
1287
|
+
this.formData[item.field]
|
|
1288
|
+
);
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1163
1291
|
return this.renderNewField(
|
|
1164
1292
|
item.field,
|
|
1165
|
-
|
|
1293
|
+
fieldConfig,
|
|
1166
1294
|
this.formData[item.field]
|
|
1167
1295
|
);
|
|
1168
1296
|
}
|
|
@@ -1184,7 +1312,7 @@ export class NodeEditor extends RapidElement {
|
|
|
1184
1312
|
config: ActionConfig | NodeConfig,
|
|
1185
1313
|
renderedFields: Set<string>
|
|
1186
1314
|
): TemplateResult {
|
|
1187
|
-
const { items, gap = '1rem' } = rowConfig;
|
|
1315
|
+
const { items, gap = '1rem', label, helpText } = rowConfig;
|
|
1188
1316
|
|
|
1189
1317
|
// Collect all fields from this row for width calculations
|
|
1190
1318
|
const fieldsInRow = this.collectFieldsFromItems(items);
|
|
@@ -1192,26 +1320,77 @@ export class NodeEditor extends RapidElement {
|
|
|
1192
1320
|
(fieldName) => config.form?.[fieldName]
|
|
1193
1321
|
);
|
|
1194
1322
|
|
|
1195
|
-
|
|
1323
|
+
// Filter for visible fields only to handle conditional visibility
|
|
1324
|
+
const visibleFields = validFields.filter((fieldName) => {
|
|
1325
|
+
const fieldConfig = config.form![fieldName];
|
|
1326
|
+
return this.isFieldVisible(fieldName, fieldConfig);
|
|
1327
|
+
});
|
|
1328
|
+
|
|
1329
|
+
if (visibleFields.length === 0) {
|
|
1196
1330
|
return html``;
|
|
1197
1331
|
}
|
|
1198
1332
|
|
|
1199
|
-
//
|
|
1200
|
-
|
|
1333
|
+
// Build a map of field flex styles
|
|
1334
|
+
// Fields with maxWidth get flex: 0 0 {maxWidth} (fixed)
|
|
1335
|
+
// Fields without maxWidth get flex: 1 1 0 (grow to fill space)
|
|
1336
|
+
const fieldFlexStyles = new Map<string, string>();
|
|
1337
|
+
visibleFields.forEach((fieldName) => {
|
|
1201
1338
|
const fieldConfig = config.form![fieldName];
|
|
1202
|
-
|
|
1339
|
+
if (fieldConfig.maxWidth) {
|
|
1340
|
+
// Fixed width field: no grow, no shrink, basis = maxWidth
|
|
1341
|
+
fieldFlexStyles.set(fieldName, `flex: 0 0 ${fieldConfig.maxWidth};`);
|
|
1342
|
+
} else {
|
|
1343
|
+
// Flexible field: grow to fill remaining space
|
|
1344
|
+
fieldFlexStyles.set(fieldName, `flex: 1 1 0;`);
|
|
1345
|
+
}
|
|
1203
1346
|
});
|
|
1204
1347
|
|
|
1348
|
+
const rowContent = html`
|
|
1349
|
+
<div class="form-row" style="display: flex; gap: ${gap};">
|
|
1350
|
+
${items.map((item) => {
|
|
1351
|
+
// Get the field name from the item
|
|
1352
|
+
const fieldName =
|
|
1353
|
+
typeof item === 'string'
|
|
1354
|
+
? item
|
|
1355
|
+
: item.type === 'field'
|
|
1356
|
+
? item.field
|
|
1357
|
+
: null;
|
|
1358
|
+
|
|
1359
|
+
// Get flex style for this field if it's a visible field
|
|
1360
|
+
const flexStyle =
|
|
1361
|
+
fieldName && fieldFlexStyles.has(fieldName)
|
|
1362
|
+
? fieldFlexStyles.get(fieldName)
|
|
1363
|
+
: '';
|
|
1364
|
+
|
|
1365
|
+
const itemContent = this.renderLayoutItem(
|
|
1366
|
+
item,
|
|
1367
|
+
config,
|
|
1368
|
+
renderedFields
|
|
1369
|
+
);
|
|
1370
|
+
|
|
1371
|
+
// Wrap in a div with flex style if we have a flex style
|
|
1372
|
+
return flexStyle
|
|
1373
|
+
? html`<div style="${flexStyle}">${itemContent}</div>`
|
|
1374
|
+
: itemContent;
|
|
1375
|
+
})}
|
|
1376
|
+
</div>
|
|
1377
|
+
`;
|
|
1378
|
+
|
|
1379
|
+
// If no label or helpText, return just the row content
|
|
1380
|
+
if (!label && !helpText) {
|
|
1381
|
+
return rowContent;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
// Otherwise, wrap with label on top, content, then helpText below (matching field pattern)
|
|
1205
1385
|
return html`
|
|
1206
|
-
<div
|
|
1207
|
-
class="form-row"
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
)}
|
|
1386
|
+
<div class="form-row-wrapper">
|
|
1387
|
+
${label ? html`<label class="form-row-label">${label}</label>` : ''}
|
|
1388
|
+
${rowContent}
|
|
1389
|
+
${helpText
|
|
1390
|
+
? html`<div class="form-row-help">
|
|
1391
|
+
${renderMarkdownInline(helpText)}
|
|
1392
|
+
</div>`
|
|
1393
|
+
: ''}
|
|
1215
1394
|
</div>
|
|
1216
1395
|
`;
|
|
1217
1396
|
}
|