@nyaruka/temba-components 0.140.0 → 0.141.1
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/.lintstagedrc.js +10 -0
- package/CHANGELOG.md +22 -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 +263 -156
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/display/FloatingTab.js +1 -1
- package/out-tsc/src/display/FloatingTab.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +1 -1
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +239 -43
- package/out-tsc/src/flow/Editor.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/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/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/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/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/wait_for_digits.js +1 -1
- package/out-tsc/src/flow/nodes/wait_for_digits.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/form/FieldRenderer.js +7 -0
- package/out-tsc/src/form/FieldRenderer.js.map +1 -1
- package/out-tsc/src/layout/Dialog.js +0 -1
- 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/live/ContactChat.js +10 -1
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/src/live/TembaChart.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 +11 -0
- package/out-tsc/src/simulator/Simulator.js.map +1 -1
- package/out-tsc/src/store/AppState.js +13 -0
- package/out-tsc/src/store/AppState.js.map +1 -1
- package/out-tsc/src/version.js +9 -0
- package/out-tsc/src/version.js.map +1 -0
- 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/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/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/temba-contact-chat.test.js +12 -0
- package/out-tsc/test/temba-contact-chat.test.js.map +1 -1
- package/out-tsc/test/temba-flow-editor.test.js +206 -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-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/rollup.components.mjs +7 -1
- 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/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/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/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/render/basic-audio-wait.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/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/FloatingTab.ts +1 -1
- package/src/flow/CanvasNode.ts +1 -1
- package/src/flow/Editor.ts +299 -88
- package/src/flow/Plumber.ts +89 -14
- package/src/flow/actions/add_contact_groups.ts +4 -1
- package/src/flow/actions/add_input_labels.ts +4 -1
- package/src/flow/actions/remove_contact_groups.ts +6 -1
- 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/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/wait_for_digits.ts +2 -1
- package/src/flow/nodes/wait_for_response.ts +1 -1
- package/src/form/FieldRenderer.ts +7 -0
- package/src/layout/Dialog.ts +0 -1
- package/src/layout/Modax.ts +19 -2
- package/src/list/ContentMenu.ts +15 -1
- package/src/live/ContactChat.ts +10 -1
- package/src/live/TembaChart.ts +1 -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 +12 -0
- package/src/store/AppState.ts +15 -0
- package/src/store/flow-definition.d.ts +1 -0
- package/src/version.ts +10 -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/remove_contact_groups.test.ts +29 -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/temba-contact-chat.test.ts +17 -0
- package/test/temba-flow-editor.test.ts +264 -0
- package/test/temba-flow-plumber.test.ts +62 -0
- package/test/temba-select.test.ts +6 -1
- package/test/utils.test.ts +4 -2
- package/web-dev-server.config.mjs +5 -1
- package/web-test-runner.config.mjs +4 -1
package/src/flow/Editor.ts
CHANGED
|
@@ -19,7 +19,14 @@ import {
|
|
|
19
19
|
import { RapidElement } from '../RapidElement';
|
|
20
20
|
import { repeat } from 'lit-html/directives/repeat.js';
|
|
21
21
|
import { CustomEventType, Workspace } from '../interfaces';
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
generateUUID,
|
|
24
|
+
postJSON,
|
|
25
|
+
fetchResults,
|
|
26
|
+
getClasses,
|
|
27
|
+
WebResponse
|
|
28
|
+
} from '../utils';
|
|
29
|
+
import { TEMBA_COMPONENTS_VERSION } from '../version';
|
|
23
30
|
import {
|
|
24
31
|
formatIssueMessage,
|
|
25
32
|
getNodeBounds,
|
|
@@ -72,7 +79,7 @@ export function findNodeForExit(
|
|
|
72
79
|
return null;
|
|
73
80
|
}
|
|
74
81
|
|
|
75
|
-
const SAVE_QUIET_TIME =
|
|
82
|
+
const SAVE_QUIET_TIME = 2000;
|
|
76
83
|
|
|
77
84
|
export interface DraggableItem {
|
|
78
85
|
uuid: string;
|
|
@@ -260,6 +267,12 @@ export class Editor extends RapidElement {
|
|
|
260
267
|
@state()
|
|
261
268
|
private isLoadingRevisions = false;
|
|
262
269
|
|
|
270
|
+
@state()
|
|
271
|
+
private isSaving = false;
|
|
272
|
+
|
|
273
|
+
@state()
|
|
274
|
+
private saveError: string | null = null;
|
|
275
|
+
|
|
263
276
|
private preRevertState: {
|
|
264
277
|
definition: FlowDefinition;
|
|
265
278
|
dirtyDate: Date | null;
|
|
@@ -361,6 +374,13 @@ export class Editor extends RapidElement {
|
|
|
361
374
|
|
|
362
375
|
static get styles() {
|
|
363
376
|
return css`
|
|
377
|
+
#editor-container {
|
|
378
|
+
position: relative;
|
|
379
|
+
flex: 1;
|
|
380
|
+
display: flex;
|
|
381
|
+
min-height: 0;
|
|
382
|
+
}
|
|
383
|
+
|
|
364
384
|
#editor {
|
|
365
385
|
overflow: scroll;
|
|
366
386
|
flex: 1;
|
|
@@ -839,6 +859,71 @@ export class Editor extends RapidElement {
|
|
|
839
859
|
color: tomato;
|
|
840
860
|
flex-shrink: 0;
|
|
841
861
|
}
|
|
862
|
+
|
|
863
|
+
.empty-flow {
|
|
864
|
+
position: sticky;
|
|
865
|
+
top: 80px;
|
|
866
|
+
left: 0;
|
|
867
|
+
right: 0;
|
|
868
|
+
height: 0;
|
|
869
|
+
display: flex;
|
|
870
|
+
justify-content: center;
|
|
871
|
+
pointer-events: none;
|
|
872
|
+
z-index: 50;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
.empty-flow-content {
|
|
876
|
+
display: flex;
|
|
877
|
+
flex-direction: column;
|
|
878
|
+
align-items: center;
|
|
879
|
+
gap: 16px;
|
|
880
|
+
text-align: center;
|
|
881
|
+
pointer-events: auto;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
.empty-flow-title {
|
|
885
|
+
font-size: 18px;
|
|
886
|
+
font-weight: 600;
|
|
887
|
+
color: #374151;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
.empty-flow-description {
|
|
891
|
+
font-size: 14px;
|
|
892
|
+
color: #6b7280;
|
|
893
|
+
max-width: 320px;
|
|
894
|
+
line-height: 1.5;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
.empty-flow-button {
|
|
898
|
+
background: var(--color-primary-dark);
|
|
899
|
+
border: none;
|
|
900
|
+
color: #fff;
|
|
901
|
+
padding: 10px 20px;
|
|
902
|
+
border-radius: var(--curvature);
|
|
903
|
+
font-size: 14px;
|
|
904
|
+
font-weight: 600;
|
|
905
|
+
cursor: pointer;
|
|
906
|
+
transition: opacity 0.2s ease;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
.empty-flow-button:hover {
|
|
910
|
+
opacity: 0.9;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
.save-indicator {
|
|
914
|
+
position: absolute;
|
|
915
|
+
top: 8px;
|
|
916
|
+
right: 16px;
|
|
917
|
+
padding: 6px 10px;
|
|
918
|
+
z-index: 10000;
|
|
919
|
+
pointer-events: none;
|
|
920
|
+
opacity: 0;
|
|
921
|
+
transition: opacity 0.15s ease-in-out;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
.save-indicator.visible {
|
|
925
|
+
opacity: 1;
|
|
926
|
+
}
|
|
842
927
|
`;
|
|
843
928
|
}
|
|
844
929
|
|
|
@@ -854,6 +939,7 @@ export class Editor extends RapidElement {
|
|
|
854
939
|
this.setupGlobalEventListeners();
|
|
855
940
|
if (changes.has('flow')) {
|
|
856
941
|
getStore().getState().fetchRevision(`/flow/revisions/${this.flow}`);
|
|
942
|
+
this.fetchRevisions();
|
|
857
943
|
}
|
|
858
944
|
|
|
859
945
|
this.plumber.on('connection:drag', (connection: any) => {
|
|
@@ -1018,10 +1104,16 @@ export class Editor extends RapidElement {
|
|
|
1018
1104
|
|
|
1019
1105
|
if (changes.has('dirtyDate')) {
|
|
1020
1106
|
if (this.dirtyDate) {
|
|
1107
|
+
this.isSaving = true;
|
|
1021
1108
|
this.debouncedSave();
|
|
1022
1109
|
}
|
|
1023
1110
|
}
|
|
1024
1111
|
|
|
1112
|
+
if (changes.has('saveError') && this.saveError) {
|
|
1113
|
+
this.showSaveErrorDialog(this.saveError);
|
|
1114
|
+
this.saveError = null;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1025
1117
|
if (changes.has('languageCode')) {
|
|
1026
1118
|
this.translationCache.clear();
|
|
1027
1119
|
}
|
|
@@ -1065,12 +1157,30 @@ export class Editor extends RapidElement {
|
|
|
1065
1157
|
}, SAVE_QUIET_TIME);
|
|
1066
1158
|
}
|
|
1067
1159
|
|
|
1160
|
+
private definitionForSave(definition: FlowDefinition): FlowDefinition {
|
|
1161
|
+
return {
|
|
1162
|
+
...definition,
|
|
1163
|
+
_ui: {
|
|
1164
|
+
...definition._ui,
|
|
1165
|
+
editor: TEMBA_COMPONENTS_VERSION
|
|
1166
|
+
}
|
|
1167
|
+
};
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1068
1170
|
private saveChanges(definitionOverride?: FlowDefinition): Promise<void> {
|
|
1069
|
-
const definition =
|
|
1070
|
-
|
|
1171
|
+
const definition = this.definitionForSave(
|
|
1172
|
+
definitionOverride || this.definition
|
|
1173
|
+
);
|
|
1174
|
+
this.isSaving = true;
|
|
1175
|
+
|
|
1071
1176
|
return getStore()
|
|
1072
1177
|
.postJSON(`/flow/revisions/${this.flow}/`, definition)
|
|
1073
1178
|
.then((response) => {
|
|
1179
|
+
if (response.status < 200 || response.status >= 300) {
|
|
1180
|
+
this.saveError = this.extractErrorMessage(response);
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1074
1184
|
// Update flow info and revision with the response data
|
|
1075
1185
|
if (response.json) {
|
|
1076
1186
|
const state = getStore().getState();
|
|
@@ -1083,17 +1193,58 @@ export class Editor extends RapidElement {
|
|
|
1083
1193
|
state.setRevision(response.json.revision.revision);
|
|
1084
1194
|
}
|
|
1085
1195
|
|
|
1086
|
-
//
|
|
1087
|
-
|
|
1088
|
-
this.fetchRevisions();
|
|
1089
|
-
}
|
|
1196
|
+
// Refresh revisions list so the tab visibility stays up to date
|
|
1197
|
+
this.fetchRevisions();
|
|
1090
1198
|
}
|
|
1199
|
+
|
|
1200
|
+
getStore().getState().setDirtyDate(null);
|
|
1091
1201
|
})
|
|
1092
1202
|
.catch((error) => {
|
|
1093
1203
|
console.error('Failed to save flow:', error);
|
|
1204
|
+
if (error instanceof Response) {
|
|
1205
|
+
this.saveError = `Server error (${error.status}). Your changes have not been saved.`;
|
|
1206
|
+
} else {
|
|
1207
|
+
this.saveError =
|
|
1208
|
+
'Unable to reach the server. Please check your connection and try again.';
|
|
1209
|
+
}
|
|
1210
|
+
})
|
|
1211
|
+
.finally(() => {
|
|
1212
|
+
this.isSaving = false;
|
|
1094
1213
|
});
|
|
1214
|
+
}
|
|
1095
1215
|
|
|
1096
|
-
|
|
1216
|
+
private extractErrorMessage(response: WebResponse): string {
|
|
1217
|
+
if (response.json) {
|
|
1218
|
+
if (typeof response.json.detail === 'string') {
|
|
1219
|
+
return response.json.detail;
|
|
1220
|
+
}
|
|
1221
|
+
if (typeof response.json.error === 'string') {
|
|
1222
|
+
return response.json.error;
|
|
1223
|
+
}
|
|
1224
|
+
if (typeof response.json.description === 'string') {
|
|
1225
|
+
return response.json.description;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
return `Save failed with status ${response.status}.`;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
private showSaveErrorDialog(message: string): void {
|
|
1232
|
+
const dialog = document.createElement('temba-dialog') as Dialog;
|
|
1233
|
+
dialog.header = 'Save Failed';
|
|
1234
|
+
dialog.primaryButtonName = '';
|
|
1235
|
+
dialog.cancelButtonName = 'Dismiss';
|
|
1236
|
+
|
|
1237
|
+
const content = document.createElement('div');
|
|
1238
|
+
content.style.cssText = 'padding: 20px; font-size: 14px; line-height: 1.5;';
|
|
1239
|
+
content.textContent = message;
|
|
1240
|
+
dialog.appendChild(content);
|
|
1241
|
+
|
|
1242
|
+
document.body.appendChild(dialog);
|
|
1243
|
+
dialog.open = true;
|
|
1244
|
+
|
|
1245
|
+
dialog.addEventListener('temba-dialog-hidden', () => {
|
|
1246
|
+
document.body.removeChild(dialog);
|
|
1247
|
+
});
|
|
1097
1248
|
}
|
|
1098
1249
|
|
|
1099
1250
|
private startActivityFetching(): void {
|
|
@@ -1129,6 +1280,11 @@ export class Editor extends RapidElement {
|
|
|
1129
1280
|
}
|
|
1130
1281
|
const state = store.getState();
|
|
1131
1282
|
state.fetchActivity(activityEndpoint).then(() => {
|
|
1283
|
+
// Guard against responses arriving after the editor is disconnected
|
|
1284
|
+
if (!this.isConnected) {
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1132
1288
|
// Schedule next fetch with exponential backoff (max 5 minutes)
|
|
1133
1289
|
this.activityInterval = Math.min(60000 * 5, this.activityInterval + 100);
|
|
1134
1290
|
|
|
@@ -1165,6 +1321,10 @@ export class Editor extends RapidElement {
|
|
|
1165
1321
|
if (canvas) {
|
|
1166
1322
|
canvas.removeEventListener('contextmenu', this.boundCanvasContextMenu);
|
|
1167
1323
|
}
|
|
1324
|
+
|
|
1325
|
+
// Clear all flow-specific data from the store so stale data
|
|
1326
|
+
// isn't briefly visible when a different flow is opened.
|
|
1327
|
+
zustand.getState().clearFlowData();
|
|
1168
1328
|
}
|
|
1169
1329
|
|
|
1170
1330
|
private setupGlobalEventListeners(): void {
|
|
@@ -2023,6 +2183,28 @@ export class Editor extends RapidElement {
|
|
|
2023
2183
|
}
|
|
2024
2184
|
}
|
|
2025
2185
|
|
|
2186
|
+
private handleEmptyFlowClick(event: MouseEvent): void {
|
|
2187
|
+
const editor = this.querySelector('#editor') as HTMLElement;
|
|
2188
|
+
if (!editor) return;
|
|
2189
|
+
|
|
2190
|
+
// Scroll to top-left
|
|
2191
|
+
editor.scrollTo({ left: 0, top: 0, behavior: 'smooth' });
|
|
2192
|
+
|
|
2193
|
+
// Place node at top-left of the canvas
|
|
2194
|
+
const nodeLeft = 0;
|
|
2195
|
+
const nodeTop = 0;
|
|
2196
|
+
|
|
2197
|
+
const canvasMenu = this.querySelector('temba-canvas-menu') as CanvasMenu;
|
|
2198
|
+
if (canvasMenu) {
|
|
2199
|
+
const button = event.currentTarget as HTMLElement;
|
|
2200
|
+
const rect = button.getBoundingClientRect();
|
|
2201
|
+
const menuWidth = 265;
|
|
2202
|
+
const menuX = rect.left + rect.width / 2 - menuWidth / 2;
|
|
2203
|
+
const menuY = rect.bottom + 8;
|
|
2204
|
+
canvasMenu.show(menuX, menuY, { x: nodeLeft, y: nodeTop }, false);
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2026
2208
|
private handleCanvasMenuSelection(event: CustomEvent): void {
|
|
2027
2209
|
const selection = event.detail as CanvasMenuSelection;
|
|
2028
2210
|
const store = getStore();
|
|
@@ -3424,7 +3606,7 @@ export class Editor extends RapidElement {
|
|
|
3424
3606
|
}
|
|
3425
3607
|
|
|
3426
3608
|
private renderIssuesTab(): TemplateResult | string {
|
|
3427
|
-
if (!this.flowIssues?.length) return '';
|
|
3609
|
+
if (!this.flowIssues?.length || !this.revisionsWindowHidden) return '';
|
|
3428
3610
|
return html`
|
|
3429
3611
|
<temba-floating-tab
|
|
3430
3612
|
id="issues-tab"
|
|
@@ -3469,6 +3651,7 @@ export class Editor extends RapidElement {
|
|
|
3469
3651
|
}
|
|
3470
3652
|
|
|
3471
3653
|
private renderRevisionsTab(): TemplateResult | string {
|
|
3654
|
+
if (this.revisions.length <= 1) return '';
|
|
3472
3655
|
return html`
|
|
3473
3656
|
<temba-floating-tab
|
|
3474
3657
|
id="revisions-tab"
|
|
@@ -3762,6 +3945,8 @@ export class Editor extends RapidElement {
|
|
|
3762
3945
|
}
|
|
3763
3946
|
|
|
3764
3947
|
private renderLocalizationTab(): TemplateResult | string {
|
|
3948
|
+
if (!this.revisionsWindowHidden) return '';
|
|
3949
|
+
if (this.definition?.nodes.length === 0) return '';
|
|
3765
3950
|
const languages = this.getLocalizationLanguages();
|
|
3766
3951
|
if (!languages.length) {
|
|
3767
3952
|
return html``;
|
|
@@ -3834,88 +4019,114 @@ export class Editor extends RapidElement {
|
|
|
3834
4019
|
return html`${style} ${this.renderIssuesWindow()}
|
|
3835
4020
|
${this.renderRevisionsWindow()} ${this.renderLocalizationWindow()}
|
|
3836
4021
|
${this.renderAutoTranslateDialog()}
|
|
3837
|
-
<div id="editor">
|
|
3838
|
-
<div
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
4022
|
+
<div id="editor-container">
|
|
4023
|
+
<div id="editor">
|
|
4024
|
+
${this.definition &&
|
|
4025
|
+
this.definition.nodes.length === 0 &&
|
|
4026
|
+
!this.isReadOnly()
|
|
4027
|
+
? html`<div class="empty-flow">
|
|
4028
|
+
<div class="empty-flow-content">
|
|
4029
|
+
<div class="empty-flow-title">This flow is empty</div>
|
|
4030
|
+
<div class="empty-flow-description">
|
|
4031
|
+
Get started by adding your first action or split to define
|
|
4032
|
+
how this flow will work.
|
|
4033
|
+
</div>
|
|
4034
|
+
<button
|
|
4035
|
+
class="empty-flow-button"
|
|
4036
|
+
@click=${this.handleEmptyFlowClick}
|
|
4037
|
+
>
|
|
4038
|
+
Add first step
|
|
4039
|
+
</button>
|
|
4040
|
+
</div>
|
|
4041
|
+
</div>`
|
|
4042
|
+
: ''}
|
|
3844
4043
|
<div
|
|
3845
|
-
id="
|
|
3846
|
-
class="${
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
!!this.viewingRevision || this.isTranslating
|
|
3850
|
-
})}"
|
|
4044
|
+
id="grid"
|
|
4045
|
+
class="${this.viewingRevision ? 'viewing-revision' : ''}"
|
|
4046
|
+
style="min-width:100%;width:${this.canvasSize
|
|
4047
|
+
.width}px; height:${this.canvasSize.height}px"
|
|
3851
4048
|
>
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
this.
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
.
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
4049
|
+
<div
|
|
4050
|
+
id="canvas"
|
|
4051
|
+
class="${getClasses({
|
|
4052
|
+
'viewing-revision': !!this.viewingRevision,
|
|
4053
|
+
'read-only-connections':
|
|
4054
|
+
!!this.viewingRevision || this.isTranslating
|
|
4055
|
+
})}"
|
|
4056
|
+
>
|
|
4057
|
+
${this.definition
|
|
4058
|
+
? repeat(
|
|
4059
|
+
[...this.definition.nodes].sort((a, b) =>
|
|
4060
|
+
a.uuid.localeCompare(b.uuid)
|
|
4061
|
+
),
|
|
4062
|
+
(node) => node.uuid,
|
|
4063
|
+
(node) => {
|
|
4064
|
+
const position = this.definition._ui?.nodes[node.uuid]
|
|
4065
|
+
?.position || {
|
|
4066
|
+
left: 0,
|
|
4067
|
+
top: 0
|
|
4068
|
+
};
|
|
4069
|
+
|
|
4070
|
+
const dragging =
|
|
4071
|
+
this.isDragging &&
|
|
4072
|
+
this.currentDragItem?.uuid === node.uuid;
|
|
4073
|
+
|
|
4074
|
+
const selected = this.selectedItems.has(node.uuid);
|
|
4075
|
+
|
|
4076
|
+
// first node is the flow start (nodes are sorted by position)
|
|
4077
|
+
const isFlowStart =
|
|
4078
|
+
this.definition.nodes.length > 0 &&
|
|
4079
|
+
this.definition.nodes[0].uuid === node.uuid;
|
|
4080
|
+
|
|
4081
|
+
return html`<temba-flow-node
|
|
4082
|
+
class="draggable ${dragging
|
|
4083
|
+
? 'dragging'
|
|
4084
|
+
: ''} ${selected ? 'selected' : ''} ${isFlowStart
|
|
4085
|
+
? 'flow-start'
|
|
4086
|
+
: ''}"
|
|
4087
|
+
@mousedown=${this.handleMouseDown.bind(this)}
|
|
4088
|
+
uuid=${node.uuid}
|
|
4089
|
+
data-node-uuid=${node.uuid}
|
|
4090
|
+
style="left:${position.left}px; top:${position.top}px;transition: all 0.2s ease-in-out;"
|
|
4091
|
+
.plumber=${this.plumber}
|
|
4092
|
+
.node=${node}
|
|
4093
|
+
.ui=${this.definition._ui.nodes[node.uuid]}
|
|
4094
|
+
@temba-node-deleted=${(event) => {
|
|
4095
|
+
this.deleteNodes([event.detail.uuid]);
|
|
4096
|
+
}}
|
|
4097
|
+
></temba-flow-node>`;
|
|
4098
|
+
}
|
|
4099
|
+
)
|
|
4100
|
+
: html`<temba-loading></temba-loading>`}
|
|
4101
|
+
${repeat(
|
|
4102
|
+
Object.entries(stickies),
|
|
4103
|
+
([uuid]) => uuid,
|
|
4104
|
+
([uuid, sticky]) => {
|
|
4105
|
+
const position = sticky.position || { left: 0, top: 0 };
|
|
4106
|
+
const dragging =
|
|
4107
|
+
this.isDragging && this.currentDragItem?.uuid === uuid;
|
|
4108
|
+
const selected = this.selectedItems.has(uuid);
|
|
4109
|
+
return html`<temba-sticky-note
|
|
4110
|
+
class="draggable ${dragging ? 'dragging' : ''} ${selected
|
|
4111
|
+
? 'selected'
|
|
4112
|
+
: ''}"
|
|
4113
|
+
@mousedown=${this.handleMouseDown.bind(this)}
|
|
4114
|
+
style="left:${position.left}px; top:${position.top}px;"
|
|
4115
|
+
uuid=${uuid}
|
|
4116
|
+
.data=${sticky}
|
|
4117
|
+
.dragging=${dragging}
|
|
4118
|
+
.selected=${selected}
|
|
4119
|
+
></temba-sticky-note>`;
|
|
4120
|
+
}
|
|
4121
|
+
)}
|
|
4122
|
+
${this.renderSelectionBox()} ${this.renderCanvasDropPreview()}
|
|
4123
|
+
${this.renderConnectionPlaceholder()}
|
|
4124
|
+
</div>
|
|
3917
4125
|
</div>
|
|
3918
4126
|
</div>
|
|
4127
|
+
<div class="save-indicator ${this.isSaving ? 'visible' : ''}">
|
|
4128
|
+
<temba-loading units="3" size="8"></temba-loading>
|
|
4129
|
+
</div>
|
|
3919
4130
|
</div>
|
|
3920
4131
|
|
|
3921
4132
|
${this.editingNode || this.editingAction
|