@nyaruka/temba-components 0.129.8 → 0.129.10
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/CHANGELOG.md +37 -3
- package/demo/data/flows/sample-flow.json +186 -96
- package/demo/test-colorpicker.html +30 -0
- package/dist/temba-components.js +1126 -1111
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/events.js.map +1 -1
- package/out-tsc/src/excellent/helpers.js +2 -2
- package/out-tsc/src/excellent/helpers.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +25 -7
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +11 -1
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +133 -290
- package/out-tsc/src/flow/NodeEditor.js.map +1 -1
- package/out-tsc/src/flow/actions/add_input_labels.js +40 -0
- package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
- package/out-tsc/src/flow/actions/call_llm.js +56 -3
- package/out-tsc/src/flow/actions/call_llm.js.map +1 -1
- package/out-tsc/src/flow/actions/call_webhook.js +1 -1
- package/out-tsc/src/flow/actions/call_webhook.js.map +1 -1
- package/out-tsc/src/flow/actions/open_ticket.js +65 -3
- package/out-tsc/src/flow/actions/open_ticket.js.map +1 -1
- package/out-tsc/src/flow/actions/set_run_result.js +75 -0
- package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
- package/out-tsc/src/flow/config.js +4 -0
- package/out-tsc/src/flow/config.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +227 -0
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_ticket.js +18 -0
- package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_response.js +27 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
- package/out-tsc/src/flow/types.js +0 -65
- package/out-tsc/src/flow/types.js.map +1 -1
- package/out-tsc/src/form/ArrayEditor.js +63 -117
- package/out-tsc/src/form/ArrayEditor.js.map +1 -1
- package/out-tsc/src/form/BaseListEditor.js +4 -3
- package/out-tsc/src/form/BaseListEditor.js.map +1 -1
- package/out-tsc/src/form/Checkbox.js +77 -24
- package/out-tsc/src/form/Checkbox.js.map +1 -1
- package/out-tsc/src/form/ColorPicker.js +28 -40
- package/out-tsc/src/form/ColorPicker.js.map +1 -1
- package/out-tsc/src/form/Completion.js +44 -53
- package/out-tsc/src/form/Completion.js.map +1 -1
- package/out-tsc/src/form/Compose.js +7 -8
- package/out-tsc/src/form/Compose.js.map +1 -1
- package/out-tsc/src/form/ContactSearch.js +3 -4
- package/out-tsc/src/form/ContactSearch.js.map +1 -1
- package/out-tsc/src/form/DatePicker.js +29 -36
- package/out-tsc/src/form/DatePicker.js.map +1 -1
- package/out-tsc/src/form/{FormField.js → FieldElement.js} +81 -53
- package/out-tsc/src/form/FieldElement.js.map +1 -0
- package/out-tsc/src/form/FieldRenderer.js +306 -0
- package/out-tsc/src/form/FieldRenderer.js.map +1 -0
- package/out-tsc/src/form/ImagePicker.js +122 -126
- package/out-tsc/src/form/ImagePicker.js.map +1 -1
- package/out-tsc/src/form/KeyValueEditor.js +41 -37
- package/out-tsc/src/form/KeyValueEditor.js.map +1 -1
- package/out-tsc/src/form/MessageEditor.js +55 -63
- package/out-tsc/src/form/MessageEditor.js.map +1 -1
- package/out-tsc/src/form/TembaSlider.js +3 -3
- package/out-tsc/src/form/TembaSlider.js.map +1 -1
- package/out-tsc/src/form/TemplateEditor.js +3 -3
- package/out-tsc/src/form/TemplateEditor.js.map +1 -1
- package/out-tsc/src/form/TextInput.js +23 -27
- package/out-tsc/src/form/TextInput.js.map +1 -1
- package/out-tsc/src/form/select/Select.js +57 -35
- package/out-tsc/src/form/select/Select.js.map +1 -1
- package/out-tsc/src/form/select/UserSelect.js +8 -9
- package/out-tsc/src/form/select/UserSelect.js.map +1 -1
- package/out-tsc/src/form/select/WorkspaceSelect.js +7 -8
- package/out-tsc/src/form/select/WorkspaceSelect.js.map +1 -1
- package/out-tsc/src/live/ContactChat.js +62 -44
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/src/live/ContactFieldEditor.js.map +1 -1
- package/out-tsc/src/markdown.js +13 -11
- package/out-tsc/src/markdown.js.map +1 -1
- package/out-tsc/temba-modules.js +3 -2
- package/out-tsc/temba-modules.js.map +1 -1
- package/out-tsc/test/ActionHelper.js +2 -0
- package/out-tsc/test/ActionHelper.js.map +1 -1
- package/out-tsc/test/NodeHelper.js +148 -0
- package/out-tsc/test/NodeHelper.js.map +1 -0
- package/out-tsc/test/actions/call_llm.test.js +103 -0
- package/out-tsc/test/actions/call_llm.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_llm_categorize.test.js +532 -0
- package/out-tsc/test/nodes/split_by_llm_categorize.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_random.test.js +150 -0
- package/out-tsc/test/nodes/split_by_random.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_digits.test.js +150 -0
- package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_response.test.js +171 -0
- package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -0
- package/out-tsc/test/temba-add-input-labels.test.js +70 -0
- package/out-tsc/test/temba-add-input-labels.test.js.map +1 -0
- package/out-tsc/test/temba-checkbox.test.js +16 -0
- package/out-tsc/test/temba-checkbox.test.js.map +1 -1
- package/out-tsc/test/temba-field-renderer.test.js +296 -0
- package/out-tsc/test/temba-field-renderer.test.js.map +1 -0
- package/out-tsc/test/temba-integration-markdown.test.js +2 -4
- package/out-tsc/test/temba-integration-markdown.test.js.map +1 -1
- package/out-tsc/test/temba-markdown.test.js +1 -1
- package/out-tsc/test/temba-markdown.test.js.map +1 -1
- package/out-tsc/test/temba-node-editor.test.js +400 -0
- package/out-tsc/test/temba-node-editor.test.js.map +1 -1
- package/out-tsc/test/temba-select.test.js +6 -3
- package/out-tsc/test/temba-select.test.js.map +1 -1
- package/out-tsc/test/temba-slider.test.js +0 -1
- package/out-tsc/test/temba-slider.test.js.map +1 -1
- package/out-tsc/test/temba-webchat.test.js +1 -1
- package/out-tsc/test/temba-webchat.test.js.map +1 -1
- package/package.json +1 -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/call_llm/editor/information-extraction.png +0 -0
- package/screenshots/truth/actions/call_llm/editor/sentiment-analysis.png +0 -0
- package/screenshots/truth/actions/call_llm/editor/summarization.png +0 -0
- package/screenshots/truth/actions/call_llm/editor/translation-task.png +0 -0
- package/screenshots/truth/actions/call_llm/render/information-extraction.png +0 -0
- package/screenshots/truth/actions/call_llm/render/sentiment-analysis.png +0 -0
- package/screenshots/truth/actions/call_llm/render/summarization.png +0 -0
- package/screenshots/truth/actions/call_llm/render/translation-task.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/send_email/editor/complex-business-email.png +0 -0
- package/screenshots/truth/actions/send_email/editor/multiple-recipients.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/checkbox/checkbox-label-background-hover.png +0 -0
- package/screenshots/truth/checkbox/checkbox-no-label-no-background-hover.png +0 -0
- package/screenshots/truth/checkbox/checkbox-with-help-text.png +0 -0
- package/screenshots/truth/checkbox/checked.png +0 -0
- package/screenshots/truth/checkbox/default.png +0 -0
- package/screenshots/truth/colorpicker/default.png +0 -0
- package/screenshots/truth/colorpicker/focused.png +0 -0
- package/screenshots/truth/colorpicker/initialized.png +0 -0
- package/screenshots/truth/colorpicker/selected.png +0 -0
- package/screenshots/truth/editor/router.png +0 -0
- package/screenshots/truth/editor/send_msg.png +0 -0
- package/screenshots/truth/editor/set_contact_language.png +0 -0
- package/screenshots/truth/editor/set_contact_name.png +0 -0
- package/screenshots/truth/editor/set_run_result.png +0 -0
- package/screenshots/truth/editor/wait.png +0 -0
- package/screenshots/truth/field-renderer/checkbox-checked.png +0 -0
- package/screenshots/truth/field-renderer/checkbox-unchecked.png +0 -0
- package/screenshots/truth/field-renderer/checkbox-with-errors.png +0 -0
- package/screenshots/truth/field-renderer/context-comparison.png +0 -0
- package/screenshots/truth/field-renderer/key-value-with-label.png +0 -0
- package/screenshots/truth/field-renderer/message-editor-with-label.png +0 -0
- package/screenshots/truth/field-renderer/select-multi.png +0 -0
- package/screenshots/truth/field-renderer/select-no-label.png +0 -0
- package/screenshots/truth/field-renderer/select-with-label.png +0 -0
- package/screenshots/truth/field-renderer/text-evaluated.png +0 -0
- package/screenshots/truth/field-renderer/text-no-label.png +0 -0
- package/screenshots/truth/field-renderer/text-with-errors.png +0 -0
- package/screenshots/truth/field-renderer/text-with-label.png +0 -0
- package/screenshots/truth/field-renderer/textarea-evaluated.png +0 -0
- package/screenshots/truth/field-renderer/textarea-with-label.png +0 -0
- package/screenshots/truth/integration/checkbox-markdown-errors.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/screenshots/truth/omnibox/selected.png +0 -0
- package/screenshots/truth/run-list/basic.png +0 -0
- package/screenshots/truth/select/functions.png +0 -0
- package/screenshots/truth/select/multi-with-endpoint.png +0 -0
- package/screenshots/truth/select/search-enabled.png +0 -0
- package/src/events.ts +12 -6
- package/src/excellent/helpers.ts +2 -2
- package/src/flow/CanvasNode.ts +22 -1
- package/src/flow/Editor.ts +12 -1
- package/src/flow/NodeEditor.ts +186 -374
- package/src/flow/actions/add_input_labels.ts +45 -0
- package/src/flow/actions/call_llm.ts +57 -3
- package/src/flow/actions/call_webhook.ts +1 -1
- package/src/flow/actions/open_ticket.ts +74 -3
- package/src/flow/actions/set_run_result.ts +83 -0
- package/src/flow/config.ts +4 -0
- package/src/flow/nodes/split_by_llm_categorize.ts +277 -0
- package/src/flow/nodes/split_by_ticket.ts +19 -0
- package/src/flow/nodes/wait_for_response.ts +28 -1
- package/src/flow/types.ts +26 -127
- package/src/form/ArrayEditor.ts +79 -139
- package/src/form/BaseListEditor.ts +4 -4
- package/src/form/Checkbox.ts +81 -24
- package/src/form/ColorPicker.ts +31 -43
- package/src/form/Completion.ts +49 -56
- package/src/form/Compose.ts +8 -8
- package/src/form/ContactSearch.ts +3 -4
- package/src/form/DatePicker.ts +32 -38
- package/src/form/{FormField.ts → FieldElement.ts} +108 -55
- package/src/form/FieldRenderer.ts +466 -0
- package/src/form/ImagePicker.ts +107 -110
- package/src/form/KeyValueEditor.ts +43 -39
- package/src/form/MessageEditor.ts +61 -67
- package/src/form/TembaSlider.ts +3 -3
- package/src/form/TemplateEditor.ts +3 -3
- package/src/form/TextInput.ts +26 -29
- package/src/form/select/Select.ts +63 -37
- package/src/form/select/UserSelect.ts +10 -11
- package/src/form/select/WorkspaceSelect.ts +9 -10
- package/src/live/ContactChat.ts +62 -47
- package/src/live/ContactFieldEditor.ts +2 -2
- package/src/markdown.ts +19 -11
- package/src/store/flow-definition.d.ts +5 -2
- package/static/api/labels.json +31 -0
- package/static/api/topics.json +24 -9
- package/static/api/users.json +35 -16
- package/static/css/temba-components.css +3 -3
- package/stress-test.js +18 -13
- package/temba-modules.ts +3 -2
- package/test/ActionHelper.ts +2 -0
- package/test/NodeHelper.ts +184 -0
- package/test/actions/call_llm.test.ts +137 -0
- package/test/nodes/README.md +78 -0
- package/test/nodes/split_by_llm_categorize.test.ts +698 -0
- package/test/nodes/split_by_random.test.ts +177 -0
- package/test/nodes/wait_for_digits.test.ts +176 -0
- package/test/nodes/wait_for_response.test.ts +206 -0
- package/test/temba-add-input-labels.test.ts +87 -0
- package/test/temba-checkbox.test.ts +26 -0
- package/test/temba-field-renderer.test.ts +482 -0
- package/test/temba-integration-markdown.test.ts +2 -4
- package/test/temba-markdown.test.ts +1 -1
- package/test/temba-node-editor.test.ts +496 -0
- package/test/temba-select.test.ts +6 -6
- package/test/temba-slider.test.ts +0 -1
- package/test/temba-webchat.test.ts +1 -1
- package/test-assets/contacts/history.json +7 -20
- package/test-assets/select/llms.json +18 -0
- package/web-dev-mock.mjs +96 -6
- package/web-dev-server.config.mjs +29 -7
- package/out-tsc/src/form/FormElement.js +0 -67
- package/out-tsc/src/form/FormElement.js.map +0 -1
- package/out-tsc/src/form/FormField.js.map +0 -1
- package/out-tsc/test/temba-formfield.test.js +0 -94
- package/out-tsc/test/temba-formfield.test.js.map +0 -1
- package/src/form/FormElement.ts +0 -69
- package/test/temba-flow-editor.test.ts.backup +0 -563
- package/test/temba-formfield.test.ts +0 -121
- package/test/temba-utils-index.test.ts.backup +0 -1737
package/src/flow/NodeEditor.ts
CHANGED
|
@@ -10,17 +10,11 @@ import {
|
|
|
10
10
|
FieldConfig,
|
|
11
11
|
ActionConfig
|
|
12
12
|
} from './config';
|
|
13
|
-
import {
|
|
14
|
-
SelectFieldConfig,
|
|
15
|
-
CheckboxFieldConfig,
|
|
16
|
-
TextareaFieldConfig,
|
|
17
|
-
MessageEditorFieldConfig,
|
|
18
|
-
LayoutItem,
|
|
19
|
-
RowLayoutConfig,
|
|
20
|
-
GroupLayoutConfig
|
|
21
|
-
} from './types';
|
|
13
|
+
import { LayoutItem, RowLayoutConfig, GroupLayoutConfig } from './types';
|
|
22
14
|
import { CustomEventType } from '../interfaces';
|
|
23
15
|
import { generateUUID } from '../utils';
|
|
16
|
+
import { FieldRenderer } from '../form/FieldRenderer';
|
|
17
|
+
import { renderMarkdownInline } from '../markdown';
|
|
24
18
|
|
|
25
19
|
export class NodeEditor extends RapidElement {
|
|
26
20
|
static get styles() {
|
|
@@ -33,9 +27,9 @@ export class NodeEditor extends RapidElement {
|
|
|
33
27
|
min-width: 400px;
|
|
34
28
|
padding-bottom: 40px;
|
|
35
29
|
|
|
36
|
-
--color-bubble-bg: rgba(
|
|
37
|
-
--color-bubble-border:
|
|
38
|
-
--color-bubble-text: #
|
|
30
|
+
--color-bubble-bg: rgba(var(--primary-rgb), 0.7);
|
|
31
|
+
--color-bubble-border: rgba(0, 0, 0, 0.2);
|
|
32
|
+
--color-bubble-text: #fff;
|
|
39
33
|
}
|
|
40
34
|
|
|
41
35
|
.form-field {
|
|
@@ -105,8 +99,8 @@ export class NodeEditor extends RapidElement {
|
|
|
105
99
|
}
|
|
106
100
|
|
|
107
101
|
.form-group.has-bubble {
|
|
108
|
-
border-width:
|
|
109
|
-
border-color: var(--
|
|
102
|
+
border-width: 2px;
|
|
103
|
+
border-color: rgba(var(--primary-rgb), 0.5);
|
|
110
104
|
}
|
|
111
105
|
|
|
112
106
|
.form-group-header {
|
|
@@ -122,6 +116,7 @@ export class NodeEditor extends RapidElement {
|
|
|
122
116
|
}
|
|
123
117
|
|
|
124
118
|
.form-group.has-bubble .form-group-header {
|
|
119
|
+
background: rgba(var(--primary-rgb), 0.1);
|
|
125
120
|
}
|
|
126
121
|
|
|
127
122
|
.collapsed .form-group-header {
|
|
@@ -299,10 +294,21 @@ export class NodeEditor extends RapidElement {
|
|
|
299
294
|
|
|
300
295
|
updated(changedProperties: Map<string | number | symbol, unknown>): void {
|
|
301
296
|
super.updated(changedProperties);
|
|
302
|
-
if (
|
|
303
|
-
|
|
297
|
+
if (
|
|
298
|
+
changedProperties.has('node') ||
|
|
299
|
+
changedProperties.has('action') ||
|
|
300
|
+
changedProperties.has('nodeUI')
|
|
301
|
+
) {
|
|
302
|
+
// For action editing, we only need the action
|
|
303
|
+
if (this.action && (!this.node || !this.nodeUI)) {
|
|
304
304
|
this.openDialog();
|
|
305
|
-
}
|
|
305
|
+
}
|
|
306
|
+
// For node editing, we need both node and nodeUI
|
|
307
|
+
else if (this.node && this.nodeUI) {
|
|
308
|
+
this.openDialog();
|
|
309
|
+
}
|
|
310
|
+
// If we don't have the required data, close the dialog
|
|
311
|
+
else if (!this.action && (!this.node || !this.nodeUI)) {
|
|
306
312
|
this.isOpen = false;
|
|
307
313
|
}
|
|
308
314
|
}
|
|
@@ -323,7 +329,9 @@ export class NodeEditor extends RapidElement {
|
|
|
323
329
|
}
|
|
324
330
|
|
|
325
331
|
private initializeFormData(): void {
|
|
326
|
-
|
|
332
|
+
const nodeConfig = this.getNodeConfig();
|
|
333
|
+
|
|
334
|
+
if ((!nodeConfig || nodeConfig.type === 'execute_actions') && this.action) {
|
|
327
335
|
// Action editing mode - use action config
|
|
328
336
|
const actionConfig = ACTION_CONFIG[this.action.type];
|
|
329
337
|
|
|
@@ -331,8 +339,6 @@ export class NodeEditor extends RapidElement {
|
|
|
331
339
|
this.formData = actionConfig.toFormData(this.action);
|
|
332
340
|
} else {
|
|
333
341
|
this.formData = { ...this.action };
|
|
334
|
-
// Apply smart transformations for select fields that expect {name, value} format
|
|
335
|
-
this.applySmartSelectTransformations(actionConfig);
|
|
336
342
|
}
|
|
337
343
|
|
|
338
344
|
// Convert Record objects to array format for key-value editors
|
|
@@ -390,52 +396,35 @@ export class NodeEditor extends RapidElement {
|
|
|
390
396
|
this.formData = processed;
|
|
391
397
|
}
|
|
392
398
|
|
|
393
|
-
private
|
|
394
|
-
if
|
|
399
|
+
private isKeyValueField(fieldName: string): boolean {
|
|
400
|
+
// Check if this field is configured as a key-value type
|
|
401
|
+
const config = this.getConfig();
|
|
402
|
+
const fields = config?.form;
|
|
403
|
+
return fields?.[fieldName]?.type === 'key-value';
|
|
404
|
+
}
|
|
395
405
|
|
|
396
|
-
|
|
397
|
-
if
|
|
406
|
+
private getConfig(): ActionConfig | NodeConfig | null {
|
|
407
|
+
// If we have a node and nodeUI, check if we should use node config
|
|
408
|
+
if (this.node && this.nodeUI) {
|
|
409
|
+
const nodeConfig = this.getNodeConfig();
|
|
398
410
|
|
|
399
|
-
|
|
400
|
-
if (this.
|
|
401
|
-
|
|
402
|
-
if (
|
|
403
|
-
Array.isArray(value) &&
|
|
404
|
-
value.length > 0 &&
|
|
405
|
-
typeof value[0] === 'string'
|
|
406
|
-
) {
|
|
407
|
-
// Transform string array to select options format
|
|
408
|
-
this.formData[fieldName] = value.map((item: string) => ({
|
|
409
|
-
name: item,
|
|
410
|
-
value: item
|
|
411
|
-
}));
|
|
412
|
-
}
|
|
411
|
+
// For execute_actions nodes, defer to action editing if an action is selected
|
|
412
|
+
if (this.nodeUI.type === 'execute_actions' && this.action) {
|
|
413
|
+
return ACTION_CONFIG[this.action.type] || null;
|
|
413
414
|
}
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
415
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
return (
|
|
423
|
-
(fieldConfig.type === 'select' &&
|
|
424
|
-
(selectConfig.multi || selectConfig.tags) &&
|
|
425
|
-
// Don't transform if already has explicit transformations
|
|
426
|
-
!this.action) ||
|
|
427
|
-
!ACTION_CONFIG[this.action.type]?.toFormData
|
|
428
|
-
);
|
|
429
|
-
}
|
|
416
|
+
// For all other nodes with a config, use the node config
|
|
417
|
+
if (nodeConfig) {
|
|
418
|
+
return nodeConfig;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
430
421
|
|
|
431
|
-
|
|
432
|
-
// Check if this field is configured as a key-value type
|
|
422
|
+
// Fall back to action config if no node config or for pure action editing
|
|
433
423
|
if (this.action) {
|
|
434
|
-
|
|
435
|
-
const fields = actionConfig?.form;
|
|
436
|
-
return fields?.[fieldName]?.type === 'key-value';
|
|
424
|
+
return ACTION_CONFIG[this.action.type] || null;
|
|
437
425
|
}
|
|
438
|
-
|
|
426
|
+
|
|
427
|
+
return null;
|
|
439
428
|
}
|
|
440
429
|
|
|
441
430
|
private getNodeConfig(): NodeConfig | null {
|
|
@@ -445,16 +434,8 @@ export class NodeEditor extends RapidElement {
|
|
|
445
434
|
}
|
|
446
435
|
|
|
447
436
|
private getHeaderColor(): string {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
const actionConfig = ACTION_CONFIG[this.action.type];
|
|
451
|
-
return actionConfig?.color || '#666666';
|
|
452
|
-
} else if (this.node) {
|
|
453
|
-
// Node editing mode
|
|
454
|
-
const nodeConfig = this.getNodeConfig();
|
|
455
|
-
return nodeConfig?.color || '#666666';
|
|
456
|
-
}
|
|
457
|
-
return '#666666';
|
|
437
|
+
const config = this.getConfig();
|
|
438
|
+
return config?.color || '#666666';
|
|
458
439
|
}
|
|
459
440
|
|
|
460
441
|
private handleDialogButtonClick(event: CustomEvent): void {
|
|
@@ -545,94 +526,70 @@ export class NodeEditor extends RapidElement {
|
|
|
545
526
|
|
|
546
527
|
private validateForm(): ValidationResult {
|
|
547
528
|
const errors: { [key: string]: string } = {};
|
|
529
|
+
const config = this.getConfig();
|
|
548
530
|
|
|
549
|
-
if (
|
|
550
|
-
// Action validation using fields configuration
|
|
551
|
-
const actionConfig = ACTION_CONFIG[this.action.type];
|
|
552
|
-
|
|
531
|
+
if (config) {
|
|
553
532
|
// Check if new field configuration system is available
|
|
554
|
-
if (
|
|
555
|
-
Object.entries(
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
}
|
|
533
|
+
if (config.form) {
|
|
534
|
+
Object.entries(config.form).forEach(([fieldName, fieldConfig]) => {
|
|
535
|
+
const value = this.formData[fieldName];
|
|
536
|
+
|
|
537
|
+
// Check required fields
|
|
538
|
+
if (
|
|
539
|
+
(fieldConfig as any).required &&
|
|
540
|
+
(!value || (Array.isArray(value) && value.length === 0))
|
|
541
|
+
) {
|
|
542
|
+
errors[fieldName] = `${
|
|
543
|
+
(fieldConfig as any).label || fieldName
|
|
544
|
+
} is required.`;
|
|
545
|
+
}
|
|
568
546
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
547
|
+
// Check minLength for text fields
|
|
548
|
+
if (
|
|
549
|
+
typeof value === 'string' &&
|
|
550
|
+
(fieldConfig as any).minLength &&
|
|
551
|
+
value.length < (fieldConfig as any).minLength
|
|
552
|
+
) {
|
|
553
|
+
errors[fieldName] = `${
|
|
554
|
+
(fieldConfig as any).label || fieldName
|
|
555
|
+
} must be at least ${(fieldConfig as any).minLength} characters`;
|
|
556
|
+
}
|
|
579
557
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
}
|
|
558
|
+
// Check maxLength for text fields
|
|
559
|
+
if (
|
|
560
|
+
typeof value === 'string' &&
|
|
561
|
+
(fieldConfig as any).maxLength &&
|
|
562
|
+
value.length > (fieldConfig as any).maxLength
|
|
563
|
+
) {
|
|
564
|
+
errors[fieldName] = `${
|
|
565
|
+
(fieldConfig as any).label || fieldName
|
|
566
|
+
} must be no more than ${
|
|
567
|
+
(fieldConfig as any).maxLength
|
|
568
|
+
} characters`;
|
|
592
569
|
}
|
|
593
|
-
);
|
|
570
|
+
});
|
|
594
571
|
}
|
|
595
572
|
|
|
596
|
-
//
|
|
597
|
-
|
|
598
|
-
// Convert form data back to action for validation
|
|
599
|
-
let actionForValidation: Action;
|
|
573
|
+
// Universal validation for category arrays to check for reserved names
|
|
574
|
+
this.validateCategoryNames(errors);
|
|
600
575
|
|
|
601
|
-
|
|
602
|
-
|
|
576
|
+
// Run custom validation if available
|
|
577
|
+
if (config.validate) {
|
|
578
|
+
if (config.sanitize) {
|
|
579
|
+
config.sanitize(this.formData);
|
|
603
580
|
}
|
|
604
581
|
|
|
605
|
-
|
|
606
|
-
|
|
582
|
+
let customValidation;
|
|
583
|
+
if (this.action) {
|
|
584
|
+
customValidation = config.validate({
|
|
585
|
+
...this.action,
|
|
586
|
+
...this.formData
|
|
587
|
+
});
|
|
607
588
|
} else {
|
|
608
|
-
|
|
589
|
+
customValidation = config.validate(this.formData);
|
|
609
590
|
}
|
|
610
|
-
|
|
611
|
-
const customValidation = actionConfig.validate(actionForValidation);
|
|
612
591
|
Object.assign(errors, customValidation.errors);
|
|
613
592
|
}
|
|
614
|
-
} else if (this.node) {
|
|
615
|
-
// Node validation
|
|
616
|
-
const nodeConfig = this.getNodeConfig();
|
|
617
|
-
|
|
618
|
-
// Check required fields from node properties
|
|
619
|
-
if (nodeConfig?.properties) {
|
|
620
|
-
Object.entries(nodeConfig.properties).forEach(
|
|
621
|
-
([fieldName, fieldConfig]) => {
|
|
622
|
-
const value = this.formData[fieldName];
|
|
623
|
-
|
|
624
|
-
// Check required fields
|
|
625
|
-
if (
|
|
626
|
-
fieldConfig.required &&
|
|
627
|
-
(!value || (Array.isArray(value) && value.length === 0))
|
|
628
|
-
) {
|
|
629
|
-
errors[fieldName] = `${
|
|
630
|
-
fieldConfig.label || fieldName
|
|
631
|
-
} is required`;
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
);
|
|
635
|
-
}
|
|
636
593
|
}
|
|
637
594
|
|
|
638
595
|
// Validate key-value fields for unique keys
|
|
@@ -683,6 +640,44 @@ export class NodeEditor extends RapidElement {
|
|
|
683
640
|
});
|
|
684
641
|
}
|
|
685
642
|
|
|
643
|
+
private validateCategoryNames(errors: { [key: string]: string }): void {
|
|
644
|
+
// Universal validation for category names across all node types
|
|
645
|
+
// Prevents use of reserved category names that have special meaning in the system
|
|
646
|
+
// Define reserved category names (case-insensitive)
|
|
647
|
+
const reservedNames = [
|
|
648
|
+
'other',
|
|
649
|
+
'failure',
|
|
650
|
+
'success',
|
|
651
|
+
'all responses',
|
|
652
|
+
'no response'
|
|
653
|
+
];
|
|
654
|
+
|
|
655
|
+
// Check all form fields for category arrays
|
|
656
|
+
Object.entries(this.formData).forEach(([fieldName, value]) => {
|
|
657
|
+
if (Array.isArray(value) && fieldName === 'categories') {
|
|
658
|
+
const categories = value.filter(
|
|
659
|
+
(item: any) => item?.name && item.name.trim() !== ''
|
|
660
|
+
);
|
|
661
|
+
|
|
662
|
+
// Check for reserved names
|
|
663
|
+
const reservedUsed = categories
|
|
664
|
+
.filter((item: any) => {
|
|
665
|
+
const lowerName = item.name.trim().toLowerCase();
|
|
666
|
+
return reservedNames.includes(lowerName);
|
|
667
|
+
})
|
|
668
|
+
.map((item: any) => item.name.trim()); // Preserve original case
|
|
669
|
+
|
|
670
|
+
if (reservedUsed.length > 0) {
|
|
671
|
+
errors[
|
|
672
|
+
fieldName
|
|
673
|
+
] = `Reserved category names cannot be used: ${reservedUsed.join(
|
|
674
|
+
', '
|
|
675
|
+
)}`;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
|
|
686
681
|
private formDataToNode(formData: any = this.formData): Node {
|
|
687
682
|
if (!this.node) throw new Error('No node to update');
|
|
688
683
|
let updatedNode: Node = { ...this.node };
|
|
@@ -849,42 +844,11 @@ export class NodeEditor extends RapidElement {
|
|
|
849
844
|
if (actionConfig?.fromFormData) {
|
|
850
845
|
return actionConfig.fromFormData(formData);
|
|
851
846
|
} else {
|
|
852
|
-
//
|
|
853
|
-
|
|
854
|
-
formData,
|
|
855
|
-
actionConfig
|
|
856
|
-
);
|
|
857
|
-
return { ...this.action, ...processedFormData };
|
|
847
|
+
// Default 1:1 mapping
|
|
848
|
+
return { ...this.action, ...formData };
|
|
858
849
|
}
|
|
859
850
|
}
|
|
860
851
|
|
|
861
|
-
private reverseSmartSelectTransformations(
|
|
862
|
-
formData: any,
|
|
863
|
-
actionConfig: ActionConfig
|
|
864
|
-
): any {
|
|
865
|
-
if (!actionConfig || !actionConfig.form) return formData;
|
|
866
|
-
const processed = { ...formData };
|
|
867
|
-
|
|
868
|
-
Object.entries(actionConfig.form).forEach(([fieldName, fieldConfig]) => {
|
|
869
|
-
if (this.shouldApplySmartSelectTransformation(fieldName, fieldConfig)) {
|
|
870
|
-
const value = processed[fieldName];
|
|
871
|
-
if (
|
|
872
|
-
Array.isArray(value) &&
|
|
873
|
-
value.length > 0 &&
|
|
874
|
-
typeof value[0] === 'object' &&
|
|
875
|
-
'value' in value[0]
|
|
876
|
-
) {
|
|
877
|
-
// Transform select options format back to string array
|
|
878
|
-
processed[fieldName] = value.map(
|
|
879
|
-
(item: any) => item.value || item.name || item
|
|
880
|
-
);
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
});
|
|
884
|
-
|
|
885
|
-
return processed;
|
|
886
|
-
}
|
|
887
|
-
|
|
888
852
|
private handleFormFieldChange(propertyName: string, event: Event): void {
|
|
889
853
|
const target = event.target as any;
|
|
890
854
|
let value: any;
|
|
@@ -926,9 +890,7 @@ export class NodeEditor extends RapidElement {
|
|
|
926
890
|
}
|
|
927
891
|
|
|
928
892
|
private updateGroupCollapseStates(): void {
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
const config = ACTION_CONFIG[this.action.type];
|
|
893
|
+
const config = this.getConfig();
|
|
932
894
|
if (!config?.layout) return;
|
|
933
895
|
|
|
934
896
|
this.updateGroupCollapseStatesRecursive(config.layout);
|
|
@@ -962,9 +924,7 @@ export class NodeEditor extends RapidElement {
|
|
|
962
924
|
}
|
|
963
925
|
|
|
964
926
|
private updateComputedFields(changedFieldName: string): void {
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
const config = ACTION_CONFIG[this.action.type];
|
|
927
|
+
const config = this.getConfig();
|
|
968
928
|
if (!config?.form) return;
|
|
969
929
|
|
|
970
930
|
// Check all fields to see if any depend on the changed field
|
|
@@ -1035,181 +995,33 @@ export class NodeEditor extends RapidElement {
|
|
|
1035
995
|
value: any,
|
|
1036
996
|
errors: string[]
|
|
1037
997
|
): TemplateResult {
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
: '';
|
|
1056
|
-
|
|
1057
|
-
if (config.evaluated) {
|
|
1058
|
-
return html`<temba-completion
|
|
1059
|
-
name="${fieldName}"
|
|
1060
|
-
label="${config.label}"
|
|
1061
|
-
?required="${config.required}"
|
|
1062
|
-
.errors="${errors}"
|
|
1063
|
-
.value="${value || ''}"
|
|
1064
|
-
placeholder="${config.placeholder || ''}"
|
|
1065
|
-
textarea
|
|
1066
|
-
expressions="session"
|
|
1067
|
-
style="${minHeightStyle}"
|
|
1068
|
-
.helpText="${config.helpText || ''}"
|
|
1069
|
-
@input="${(e: Event) => this.handleFormFieldChange(fieldName, e)}"
|
|
1070
|
-
></temba-completion>`;
|
|
998
|
+
// Use FieldRenderer for consistent field rendering
|
|
999
|
+
return FieldRenderer.renderField(fieldName, config, value, {
|
|
1000
|
+
errors,
|
|
1001
|
+
onChange: (e: Event) => {
|
|
1002
|
+
// Handle different change event types
|
|
1003
|
+
if (fieldName && config.type === 'key-value') {
|
|
1004
|
+
// Special handling for key-value editor
|
|
1005
|
+
const customEvent = e as CustomEvent;
|
|
1006
|
+
if (customEvent.detail) {
|
|
1007
|
+
this.handleNewFieldChange(fieldName, customEvent.detail.value);
|
|
1008
|
+
}
|
|
1009
|
+
} else if (fieldName && config.type === 'array') {
|
|
1010
|
+
// Special handling for array editor
|
|
1011
|
+
this.handleNewFieldChange(fieldName, (e.target as any).value);
|
|
1012
|
+
} else if (fieldName && config.type === 'message-editor') {
|
|
1013
|
+
// Special handling for message editor
|
|
1014
|
+
this.handleMessageEditorChange(fieldName, e);
|
|
1071
1015
|
} else {
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
label="${config.label}"
|
|
1075
|
-
?required="${config.required}"
|
|
1076
|
-
.errors="${errors}"
|
|
1077
|
-
.value="${value || ''}"
|
|
1078
|
-
placeholder="${config.placeholder || ''}"
|
|
1079
|
-
textarea
|
|
1080
|
-
.rows="${textareaConfig.rows || 3}"
|
|
1081
|
-
style="${minHeightStyle}"
|
|
1082
|
-
.helpText="${config.helpText || ''}"
|
|
1083
|
-
@input="${(e: Event) => this.handleFormFieldChange(fieldName, e)}"
|
|
1084
|
-
></temba-textinput>`;
|
|
1016
|
+
// Default handling for most field types
|
|
1017
|
+
this.handleFormFieldChange(fieldName, e);
|
|
1085
1018
|
}
|
|
1019
|
+
},
|
|
1020
|
+
showLabel: true,
|
|
1021
|
+
additionalData: {
|
|
1022
|
+
attachments: this.formData.attachments || []
|
|
1086
1023
|
}
|
|
1087
|
-
|
|
1088
|
-
case 'select': {
|
|
1089
|
-
const selectConfig = config as SelectFieldConfig;
|
|
1090
|
-
return html`<temba-select
|
|
1091
|
-
name="${fieldName}"
|
|
1092
|
-
label="${config.label}"
|
|
1093
|
-
?required="${config.required}"
|
|
1094
|
-
.errors="${errors}"
|
|
1095
|
-
.values="${value || (selectConfig.multi ? [] : '')}"
|
|
1096
|
-
?multi="${selectConfig.multi}"
|
|
1097
|
-
?searchable="${selectConfig.searchable}"
|
|
1098
|
-
?tags="${selectConfig.tags}"
|
|
1099
|
-
?emails="${selectConfig.emails}"
|
|
1100
|
-
placeholder="${selectConfig.placeholder || ''}"
|
|
1101
|
-
maxItems="${selectConfig.maxItems || 0}"
|
|
1102
|
-
valueKey="${selectConfig.valueKey || 'value'}"
|
|
1103
|
-
nameKey="${selectConfig.nameKey || 'name'}"
|
|
1104
|
-
endpoint="${selectConfig.endpoint || ''}"
|
|
1105
|
-
.helpText="${config.helpText || ''}"
|
|
1106
|
-
flavor="${selectConfig.flavor || 'small'}"
|
|
1107
|
-
@change="${(e: Event) => this.handleFormFieldChange(fieldName, e)}"
|
|
1108
|
-
>
|
|
1109
|
-
${selectConfig.options?.map((option: any) => {
|
|
1110
|
-
if (typeof option === 'string') {
|
|
1111
|
-
return html`<temba-option
|
|
1112
|
-
name="${option}"
|
|
1113
|
-
value="${option}"
|
|
1114
|
-
></temba-option>`;
|
|
1115
|
-
} else {
|
|
1116
|
-
return html`<temba-option
|
|
1117
|
-
name="${option.label || option.name}"
|
|
1118
|
-
value="${option.value}"
|
|
1119
|
-
></temba-option>`;
|
|
1120
|
-
}
|
|
1121
|
-
})}
|
|
1122
|
-
</temba-select>`;
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
case 'key-value':
|
|
1126
|
-
return html`<div class="form-field">
|
|
1127
|
-
<label>${config.label}${config.required ? ' *' : ''}</label>
|
|
1128
|
-
<temba-key-value-editor
|
|
1129
|
-
name="${fieldName}"
|
|
1130
|
-
.value="${value || []}"
|
|
1131
|
-
.sortable="${config.sortable}"
|
|
1132
|
-
.keyPlaceholder="${config.keyPlaceholder || 'Key'}"
|
|
1133
|
-
.valuePlaceholder="${config.valuePlaceholder || 'Value'}"
|
|
1134
|
-
.minRows="${config.minRows || 0}"
|
|
1135
|
-
@change="${(e: CustomEvent) => {
|
|
1136
|
-
if (e.detail) {
|
|
1137
|
-
this.handleNewFieldChange(fieldName, e.detail.value);
|
|
1138
|
-
}
|
|
1139
|
-
}}"
|
|
1140
|
-
></temba-key-value-editor>
|
|
1141
|
-
${errors.length
|
|
1142
|
-
? html`<div class="field-errors">${errors.join(', ')}</div>`
|
|
1143
|
-
: ''}
|
|
1144
|
-
</div>`;
|
|
1145
|
-
|
|
1146
|
-
case 'array':
|
|
1147
|
-
return html`<div class="form-field">
|
|
1148
|
-
<label>${config.label}${config.required ? ' *' : ''}</label>
|
|
1149
|
-
<temba-array-editor
|
|
1150
|
-
.value="${value || []}"
|
|
1151
|
-
.itemConfig="${config.itemConfig}"
|
|
1152
|
-
.sortable="${config.sortable}"
|
|
1153
|
-
.itemLabel="${config.itemLabel || 'Item'}"
|
|
1154
|
-
.minItems="${config.minItems || 0}"
|
|
1155
|
-
.maxItems="${config.maxItems || 0}"
|
|
1156
|
-
.onItemChange="${config.onItemChange}"
|
|
1157
|
-
.isEmptyItemFn="${config.isEmptyItem}"
|
|
1158
|
-
@change="${(e: Event) =>
|
|
1159
|
-
this.handleNewFieldChange(fieldName, (e.target as any).value)}"
|
|
1160
|
-
></temba-array-editor>
|
|
1161
|
-
${errors.length
|
|
1162
|
-
? html`<div class="field-errors">${errors.join(', ')}</div>`
|
|
1163
|
-
: ''}
|
|
1164
|
-
</div>`;
|
|
1165
|
-
|
|
1166
|
-
case 'checkbox': {
|
|
1167
|
-
const checkboxConfig = config as CheckboxFieldConfig;
|
|
1168
|
-
return html`<div class="form-field">
|
|
1169
|
-
<temba-checkbox
|
|
1170
|
-
name="${fieldName}"
|
|
1171
|
-
label="${config.label}"
|
|
1172
|
-
.helpText="${config.helpText || ''}"
|
|
1173
|
-
?required="${config.required}"
|
|
1174
|
-
.errors="${errors}"
|
|
1175
|
-
?checked="${value || false}"
|
|
1176
|
-
size="${checkboxConfig.size || 1.2}"
|
|
1177
|
-
animateChange="${checkboxConfig.animateChange || 'pulse'}"
|
|
1178
|
-
@change="${(e: Event) => this.handleFormFieldChange(fieldName, e)}"
|
|
1179
|
-
></temba-checkbox>
|
|
1180
|
-
${errors.length
|
|
1181
|
-
? html`<div class="field-errors">${errors.join(', ')}</div>`
|
|
1182
|
-
: ''}
|
|
1183
|
-
</div>`;
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
|
-
case 'message-editor': {
|
|
1187
|
-
const messageConfig = config as MessageEditorFieldConfig;
|
|
1188
|
-
return html`<temba-message-editor
|
|
1189
|
-
name="${fieldName}"
|
|
1190
|
-
label="${config.label}"
|
|
1191
|
-
?required="${config.required}"
|
|
1192
|
-
.errors="${errors}"
|
|
1193
|
-
.value="${value || ''}"
|
|
1194
|
-
.attachments="${this.formData.attachments || []}"
|
|
1195
|
-
placeholder="${messageConfig.placeholder || ''}"
|
|
1196
|
-
.helpText="${config.helpText || ''}"
|
|
1197
|
-
?autogrow="${messageConfig.autogrow}"
|
|
1198
|
-
?gsm="${messageConfig.gsm}"
|
|
1199
|
-
?disableCompletion="${messageConfig.disableCompletion}"
|
|
1200
|
-
counter="${messageConfig.counter || ''}"
|
|
1201
|
-
accept="${messageConfig.accept || ''}"
|
|
1202
|
-
endpoint="${messageConfig.endpoint || ''}"
|
|
1203
|
-
max-attachments="${messageConfig.maxAttachments || 3}"
|
|
1204
|
-
minHeight="${messageConfig.minHeight || 60}"
|
|
1205
|
-
@change="${(e: Event) =>
|
|
1206
|
-
this.handleMessageEditorChange(fieldName, e)}"
|
|
1207
|
-
></temba-message-editor>`;
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
|
-
default:
|
|
1211
|
-
return html`<div>Unsupported field type: ${(config as any).type}</div>`;
|
|
1212
|
-
}
|
|
1024
|
+
});
|
|
1213
1025
|
}
|
|
1214
1026
|
|
|
1215
1027
|
private handleGroupToggle(groupLabel: string): void {
|
|
@@ -1234,9 +1046,7 @@ export class NodeEditor extends RapidElement {
|
|
|
1234
1046
|
}
|
|
1235
1047
|
|
|
1236
1048
|
private expandGroupsWithErrors(errors: { [key: string]: string }): void {
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
const config = ACTION_CONFIG[this.action.type];
|
|
1049
|
+
const config = this.getConfig();
|
|
1240
1050
|
if (!config?.layout) return;
|
|
1241
1051
|
|
|
1242
1052
|
const errorFields = new Set(Object.keys(errors));
|
|
@@ -1273,7 +1083,7 @@ export class NodeEditor extends RapidElement {
|
|
|
1273
1083
|
|
|
1274
1084
|
private renderLayoutItem(
|
|
1275
1085
|
item: LayoutItem,
|
|
1276
|
-
config: ActionConfig,
|
|
1086
|
+
config: ActionConfig | NodeConfig,
|
|
1277
1087
|
renderedFields: Set<string>
|
|
1278
1088
|
): TemplateResult {
|
|
1279
1089
|
if (typeof item === 'string') {
|
|
@@ -1310,7 +1120,7 @@ export class NodeEditor extends RapidElement {
|
|
|
1310
1120
|
|
|
1311
1121
|
private renderRow(
|
|
1312
1122
|
rowConfig: RowLayoutConfig,
|
|
1313
|
-
config: ActionConfig,
|
|
1123
|
+
config: ActionConfig | NodeConfig,
|
|
1314
1124
|
renderedFields: Set<string>
|
|
1315
1125
|
): TemplateResult {
|
|
1316
1126
|
const { items, gap = '1rem' } = rowConfig;
|
|
@@ -1347,7 +1157,7 @@ export class NodeEditor extends RapidElement {
|
|
|
1347
1157
|
|
|
1348
1158
|
private renderGroup(
|
|
1349
1159
|
groupConfig: GroupLayoutConfig,
|
|
1350
|
-
config: ActionConfig,
|
|
1160
|
+
config: ActionConfig | NodeConfig,
|
|
1351
1161
|
renderedFields: Set<string>
|
|
1352
1162
|
): TemplateResult {
|
|
1353
1163
|
const {
|
|
@@ -1434,7 +1244,9 @@ export class NodeEditor extends RapidElement {
|
|
|
1434
1244
|
<div class="form-group-info">
|
|
1435
1245
|
<div class="form-group-title">${label}</div>
|
|
1436
1246
|
${helpText
|
|
1437
|
-
? html`<div class="form-group-help"
|
|
1247
|
+
? html`<div class="form-group-help">
|
|
1248
|
+
${renderMarkdownInline(helpText)}
|
|
1249
|
+
</div>`
|
|
1438
1250
|
: ''}
|
|
1439
1251
|
</div>
|
|
1440
1252
|
${groupHasErrors
|
|
@@ -1500,7 +1312,7 @@ export class NodeEditor extends RapidElement {
|
|
|
1500
1312
|
|
|
1501
1313
|
private renderFieldRow(
|
|
1502
1314
|
rowConfig: RowLayoutConfig,
|
|
1503
|
-
config: ActionConfig
|
|
1315
|
+
config: ActionConfig | NodeConfig
|
|
1504
1316
|
): TemplateResult {
|
|
1505
1317
|
// This method is deprecated - use renderRow instead
|
|
1506
1318
|
return this.renderRow(rowConfig, config, new Set());
|
|
@@ -1508,7 +1320,7 @@ export class NodeEditor extends RapidElement {
|
|
|
1508
1320
|
|
|
1509
1321
|
private renderFieldGroup(
|
|
1510
1322
|
groupConfig: GroupLayoutConfig,
|
|
1511
|
-
config: ActionConfig
|
|
1323
|
+
config: ActionConfig | NodeConfig
|
|
1512
1324
|
): TemplateResult {
|
|
1513
1325
|
// This method is deprecated - use renderGroup instead
|
|
1514
1326
|
return this.renderGroup(groupConfig, config, new Set());
|
|
@@ -1556,13 +1368,9 @@ export class NodeEditor extends RapidElement {
|
|
|
1556
1368
|
}
|
|
1557
1369
|
|
|
1558
1370
|
private renderFields(): TemplateResult {
|
|
1559
|
-
|
|
1560
|
-
return html` <div>No action selected</div> `;
|
|
1561
|
-
}
|
|
1562
|
-
|
|
1563
|
-
const config = ACTION_CONFIG[this.action.type];
|
|
1371
|
+
const config = this.getConfig();
|
|
1564
1372
|
if (!config) {
|
|
1565
|
-
return html` <div>No configuration available
|
|
1373
|
+
return html` <div>No configuration available</div> `;
|
|
1566
1374
|
}
|
|
1567
1375
|
|
|
1568
1376
|
// Use the new fields configuration system
|
|
@@ -1603,7 +1411,12 @@ export class NodeEditor extends RapidElement {
|
|
|
1603
1411
|
}
|
|
1604
1412
|
}
|
|
1605
1413
|
|
|
1606
|
-
|
|
1414
|
+
// Fallback for configs without form configuration
|
|
1415
|
+
if (this.action) {
|
|
1416
|
+
return html` <div>No form configuration available for this action</div> `;
|
|
1417
|
+
} else {
|
|
1418
|
+
return html` <div>No form configuration available for this node</div> `;
|
|
1419
|
+
}
|
|
1607
1420
|
}
|
|
1608
1421
|
|
|
1609
1422
|
private renderActionSection(): TemplateResult {
|
|
@@ -1676,12 +1489,11 @@ export class NodeEditor extends RapidElement {
|
|
|
1676
1489
|
}
|
|
1677
1490
|
|
|
1678
1491
|
const headerColor = this.getHeaderColor();
|
|
1679
|
-
const
|
|
1680
|
-
const actionConfig = ACTION_CONFIG[this.action?.type];
|
|
1492
|
+
const config = this.getConfig();
|
|
1681
1493
|
|
|
1682
1494
|
return html`
|
|
1683
1495
|
<temba-dialog
|
|
1684
|
-
header="${
|
|
1496
|
+
header="${config?.name || 'Edit'}"
|
|
1685
1497
|
.open="${this.isOpen}"
|
|
1686
1498
|
@temba-button-clicked=${this.handleDialogButtonClick}
|
|
1687
1499
|
primaryButtonName="Save"
|
|
@@ -1690,7 +1502,7 @@ export class NodeEditor extends RapidElement {
|
|
|
1690
1502
|
>
|
|
1691
1503
|
<div class="node-editor-form">
|
|
1692
1504
|
${this.renderFields()}
|
|
1693
|
-
${
|
|
1505
|
+
${this.getNodeConfig()?.router?.configurable
|
|
1694
1506
|
? this.renderRouterSection()
|
|
1695
1507
|
: null}
|
|
1696
1508
|
</div>
|