@nyaruka/temba-components 0.130.1 → 0.130.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/CHANGELOG.md +28 -4
- package/DEV_DATA.md +89 -0
- package/demo/data/flows/food-order.json +4 -4
- package/demo/data/flows/sample-flow.json +132 -147
- package/dist/temba-components.js +764 -628
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/display/Chat.js +5 -3
- package/out-tsc/src/display/Chat.js.map +1 -1
- package/out-tsc/src/events.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +83 -78
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +1 -0
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +47 -3
- package/out-tsc/src/flow/NodeEditor.js.map +1 -1
- package/out-tsc/src/flow/actions/add_contact_urn.js +1 -1
- package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_channel.js +1 -1
- package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_field.js +2 -1
- package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_language.js +3 -1
- package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_name.js +1 -1
- package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_status.js +17 -14
- package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
- package/out-tsc/src/flow/actions/set_run_result.js +1 -1
- package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_llm.js +12 -12
- package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js +609 -6
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
- package/out-tsc/src/flow/operators.js +194 -0
- package/out-tsc/src/flow/operators.js.map +1 -0
- package/out-tsc/src/flow/types.js.map +1 -1
- package/out-tsc/src/form/ArrayEditor.js +84 -19
- package/out-tsc/src/form/ArrayEditor.js.map +1 -1
- package/out-tsc/src/form/Checkbox.js +12 -0
- package/out-tsc/src/form/Checkbox.js.map +1 -1
- package/out-tsc/src/form/FieldRenderer.js +13 -3
- package/out-tsc/src/form/FieldRenderer.js.map +1 -1
- package/out-tsc/src/form/TextInput.js +20 -1
- package/out-tsc/src/form/TextInput.js.map +1 -1
- package/out-tsc/src/form/select/Select.js +7 -0
- package/out-tsc/src/form/select/Select.js.map +1 -1
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/src/layout/Dialog.js +3 -4
- package/out-tsc/src/layout/Dialog.js.map +1 -1
- package/out-tsc/src/list/RunList.js +2 -2
- package/out-tsc/src/list/RunList.js.map +1 -1
- package/out-tsc/src/live/ContactChat.js +114 -34
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/src/live/ContactDetails.js +7 -0
- package/out-tsc/src/live/ContactDetails.js.map +1 -1
- package/out-tsc/src/live/ContactNameFetch.js +1 -1
- package/out-tsc/src/live/ContactNameFetch.js.map +1 -1
- package/out-tsc/test/NodeHelper.js +25 -27
- package/out-tsc/test/NodeHelper.js.map +1 -1
- package/out-tsc/test/nodes/split_by_llm.test.js +12 -4
- package/out-tsc/test/nodes/split_by_llm.test.js.map +1 -1
- package/out-tsc/test/nodes/split_by_llm_categorize.test.js +101 -91
- package/out-tsc/test/nodes/split_by_llm_categorize.test.js.map +1 -1
- package/out-tsc/test/nodes/split_by_random.test.js +120 -112
- package/out-tsc/test/nodes/split_by_random.test.js.map +1 -1
- package/out-tsc/test/nodes/wait_for_digits.test.js +131 -111
- package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -1
- package/out-tsc/test/nodes/wait_for_response.test.js +549 -85
- package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -1
- package/out-tsc/test/temba-checkbox.test.js +32 -32
- package/out-tsc/test/temba-checkbox.test.js.map +1 -1
- package/out-tsc/test/temba-contact-chat.test.js +2 -1
- package/out-tsc/test/temba-contact-chat.test.js.map +1 -1
- package/out-tsc/test/temba-dropdown.test.js +0 -4
- package/out-tsc/test/temba-dropdown.test.js.map +1 -1
- package/out-tsc/test/temba-flow-editor-node.test.js +9 -4
- package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
- package/out-tsc/test/temba-integration-markdown.test.js +13 -15
- package/out-tsc/test/temba-integration-markdown.test.js.map +1 -1
- package/out-tsc/test/temba-node-editor.test.js +5 -38
- package/out-tsc/test/temba-node-editor.test.js.map +1 -1
- package/out-tsc/test/temba-run-list.test.js +2 -2
- package/out-tsc/test/temba-run-list.test.js.map +1 -1
- package/out-tsc/test/utils.test.js +2 -1
- package/out-tsc/test/utils.test.js.map +1 -1
- package/package.json +6 -2
- 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/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_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/checkbox/checkbox-label-background-hover.png +0 -0
- package/screenshots/truth/checkbox/checkbox-whitespace-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/editor/wait.png +0 -0
- package/screenshots/truth/integration/textinput-markdown-errors.png +0 -0
- package/screenshots/truth/lightbox/img-zoomed.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/screenshots/truth/run-list/basic.png +0 -0
- package/screenshots/truth/templates/default.png +0 -0
- package/screenshots/truth/wait-for-response/rules-editor.png +0 -0
- package/screenshots/truth/wait-for-response/timeout-editor-unchecked.png +0 -0
- package/screenshots/truth/wait-for-response/timeout-editor.png +0 -0
- package/scripts/dev-data-sync.mjs +182 -0
- package/src/display/Chat.ts +6 -4
- package/src/events.ts +6 -5
- package/src/flow/CanvasNode.ts +89 -79
- package/src/flow/Editor.ts +1 -0
- package/src/flow/NodeEditor.ts +55 -3
- package/src/flow/actions/add_contact_urn.ts +1 -1
- package/src/flow/actions/set_contact_channel.ts +1 -1
- package/src/flow/actions/set_contact_field.ts +2 -1
- package/src/flow/actions/set_contact_language.ts +3 -1
- package/src/flow/actions/set_contact_name.ts +1 -1
- package/src/flow/actions/set_contact_status.ts +18 -18
- package/src/flow/actions/set_run_result.ts +1 -1
- package/src/flow/nodes/split_by_llm.ts +14 -13
- package/src/flow/nodes/wait_for_response.ts +717 -5
- package/src/flow/operators.ts +215 -0
- package/src/flow/types.ts +10 -2
- package/src/form/ArrayEditor.ts +117 -37
- package/src/form/Checkbox.ts +12 -0
- package/src/form/FieldRenderer.ts +24 -3
- package/src/form/TextInput.ts +19 -1
- package/src/form/select/Select.ts +7 -0
- package/src/interfaces.ts +1 -1
- package/src/layout/Dialog.ts +4 -4
- package/src/list/RunList.ts +2 -2
- package/src/live/ContactChat.ts +144 -58
- package/src/live/ContactDetails.ts +7 -0
- package/src/live/ContactNameFetch.ts +1 -1
- package/static/api/labels.json +6 -1
- package/test/NodeHelper.ts +38 -40
- package/test/nodes/split_by_llm.test.ts +43 -32
- package/test/nodes/split_by_llm_categorize.test.ts +130 -120
- package/test/nodes/split_by_random.test.ts +136 -128
- package/test/nodes/wait_for_digits.test.ts +147 -127
- package/test/nodes/wait_for_response.test.ts +657 -104
- package/test/temba-checkbox.test.ts +36 -32
- package/test/temba-contact-chat.test.ts +2 -1
- package/test/temba-dropdown.test.ts +0 -12
- package/test/temba-flow-editor-node.test.ts +11 -4
- package/test/temba-integration-markdown.test.ts +16 -17
- package/test/temba-node-editor.test.ts +5 -43
- package/test/temba-run-list.test.ts +2 -2
- package/test/utils.test.ts +2 -1
- package/test-assets/list/runs.json +8 -8
- package/web-dev-mock.mjs +86 -30
- package/web-dev-server.config.mjs +272 -31
- package/screenshots/truth/dropdown/bottom-edge-collision.png +0 -0
- package/screenshots/truth/dropdown/right-edge-collision.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/integration/checkbox-markdown-errors.png +0 -0
package/src/live/ContactChat.ts
CHANGED
|
@@ -73,7 +73,7 @@ export enum Events {
|
|
|
73
73
|
OPTIN_STOPPED = 'optin_stopped',
|
|
74
74
|
RUN_ENDED = 'run_ended',
|
|
75
75
|
RUN_STARTED = 'run_started',
|
|
76
|
-
|
|
76
|
+
TICKET_ASSIGNEE_CHANGED = 'ticket_assignee_changed',
|
|
77
77
|
TICKET_CLOSED = 'ticket_closed',
|
|
78
78
|
TICKET_NOTE_ADDED = 'ticket_note_added',
|
|
79
79
|
TICKET_OPENED = 'ticket_opened',
|
|
@@ -81,32 +81,43 @@ export enum Events {
|
|
|
81
81
|
TICKET_TOPIC_CHANGED = 'ticket_topic_changed',
|
|
82
82
|
|
|
83
83
|
// deprecated
|
|
84
|
-
CHANNEL_EVENT = 'channel_event'
|
|
84
|
+
CHANNEL_EVENT = 'channel_event',
|
|
85
|
+
TICKET_ASSIGNED = 'ticket_assigned'
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
const renderInfoList = (
|
|
88
|
+
const renderInfoList = (
|
|
89
|
+
singular: string,
|
|
90
|
+
plural: string,
|
|
91
|
+
items: any[]
|
|
92
|
+
): TemplateResult => {
|
|
88
93
|
if (items.length === 1) {
|
|
89
|
-
return
|
|
94
|
+
return html`<div>${singular} <strong>${items[0].name}</strong></div>`;
|
|
90
95
|
} else {
|
|
91
|
-
const list = items.map((item) =>
|
|
96
|
+
const list = items.map((item) => item.name);
|
|
92
97
|
if (list.length === 2) {
|
|
93
|
-
return
|
|
98
|
+
return html`<div>
|
|
99
|
+
${plural} <strong>${list[0]}</strong> and <strong>${list[1]}</strong>
|
|
100
|
+
</div>`;
|
|
94
101
|
} else {
|
|
95
102
|
const last = list.pop();
|
|
96
|
-
|
|
103
|
+
const middle = list.map(
|
|
104
|
+
(name, index) =>
|
|
105
|
+
html`<strong>${name}</strong>${index < list.length - 1 ? ', ' : ''}`
|
|
106
|
+
);
|
|
107
|
+
return html`<div>${plural} ${middle}, and <strong>${last}</strong></div>`;
|
|
97
108
|
}
|
|
98
109
|
}
|
|
99
110
|
};
|
|
100
111
|
|
|
101
|
-
const renderChannelEvent = (event: ChannelEvent):
|
|
112
|
+
const renderChannelEvent = (event: ChannelEvent): TemplateResult => {
|
|
102
113
|
if (event.channel_event_type === 'welcome_message') {
|
|
103
|
-
return
|
|
114
|
+
return html`<div>Welcome message sent</div>`;
|
|
104
115
|
} else if (event.event.type === 'stop_contact') {
|
|
105
|
-
return
|
|
116
|
+
return html`<div>Stopped</div>`;
|
|
106
117
|
}
|
|
107
118
|
};
|
|
108
119
|
|
|
109
|
-
const renderRunEvent = (event: RunEvent):
|
|
120
|
+
const renderRunEvent = (event: RunEvent): TemplateResult => {
|
|
110
121
|
let verb = 'Started';
|
|
111
122
|
if (event.type === Events.RUN_ENDED) {
|
|
112
123
|
if (event.status === 'completed') {
|
|
@@ -118,57 +129,117 @@ const renderRunEvent = (event: RunEvent): string => {
|
|
|
118
129
|
}
|
|
119
130
|
}
|
|
120
131
|
|
|
121
|
-
return
|
|
132
|
+
return html`<div>
|
|
133
|
+
${verb}
|
|
134
|
+
<a href="/flow/editor/${event.flow.uuid}/"
|
|
135
|
+
><strong>${event.flow.name}</strong></a
|
|
136
|
+
>
|
|
137
|
+
</div>`;
|
|
122
138
|
};
|
|
123
139
|
|
|
124
|
-
const renderChatStartedEvent = (event: ChatStartedEvent):
|
|
140
|
+
const renderChatStartedEvent = (event: ChatStartedEvent): TemplateResult => {
|
|
125
141
|
if (event.params) {
|
|
126
|
-
return
|
|
142
|
+
return html`<div>Chat referral</div>`;
|
|
127
143
|
} else {
|
|
128
|
-
return
|
|
144
|
+
return html`<div>Chat started</div>`;
|
|
129
145
|
}
|
|
130
146
|
};
|
|
131
147
|
|
|
132
|
-
const renderUpdateEvent = (event: UpdateFieldEvent):
|
|
148
|
+
const renderUpdateEvent = (event: UpdateFieldEvent): TemplateResult => {
|
|
133
149
|
return event.value
|
|
134
|
-
?
|
|
135
|
-
|
|
150
|
+
? html`<div>
|
|
151
|
+
Updated <strong>${event.field.name}</strong> to
|
|
152
|
+
<strong>${event.value.text}</strong>
|
|
153
|
+
</div>`
|
|
154
|
+
: html`<div>Cleared <strong>${event.field.name}</strong></div>`;
|
|
136
155
|
};
|
|
137
156
|
|
|
138
|
-
const renderNameChanged = (event: NameChangedEvent):
|
|
139
|
-
return
|
|
157
|
+
const renderNameChanged = (event: NameChangedEvent): TemplateResult => {
|
|
158
|
+
return html`<div>
|
|
159
|
+
Updated <strong>name</strong> to <strong>${event.name}</strong>
|
|
160
|
+
</div>`;
|
|
140
161
|
};
|
|
141
162
|
|
|
142
|
-
const renderContactURNsChanged = (event: URNsChangedEvent):
|
|
143
|
-
return
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
163
|
+
const renderContactURNsChanged = (event: URNsChangedEvent): TemplateResult => {
|
|
164
|
+
return html`<div>
|
|
165
|
+
Updated <strong>URNs</strong> to
|
|
166
|
+
${oxfordFn(
|
|
167
|
+
event.urns,
|
|
168
|
+
(urn: string) => html`<strong>${urn.split(':')[1].split('?')[0]}</strong>`
|
|
169
|
+
)}
|
|
170
|
+
</div>`;
|
|
147
171
|
};
|
|
148
172
|
|
|
149
173
|
export const renderTicketAction = (
|
|
150
174
|
event: TicketEvent,
|
|
151
175
|
action: string
|
|
152
|
-
):
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
176
|
+
): TemplateResult => {
|
|
177
|
+
const ticketUUID = event.ticket?.uuid || event.ticket_uuid;
|
|
178
|
+
const userDisplay = event.created_by
|
|
179
|
+
? getUserDisplay(event.created_by)
|
|
180
|
+
: event._user?.name;
|
|
181
|
+
|
|
182
|
+
if (userDisplay) {
|
|
183
|
+
return html`<div>
|
|
184
|
+
<strong>${userDisplay}</strong> ${action} a
|
|
185
|
+
<strong><a href="/ticket/all/closed/${ticketUUID}/">ticket</a></strong>
|
|
186
|
+
</div>`;
|
|
187
|
+
}
|
|
188
|
+
return html`<div>
|
|
189
|
+
A
|
|
190
|
+
<strong><a href="/ticket/all/closed/${ticketUUID}/">ticket</a></strong> was
|
|
191
|
+
<strong>${action}</strong>
|
|
192
|
+
</div>`;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export const renderTicketAssigneeChanged = (
|
|
196
|
+
event: TicketEvent
|
|
197
|
+
): TemplateResult => {
|
|
198
|
+
if (event._user) {
|
|
199
|
+
if (event.assignee) {
|
|
200
|
+
return html`<div>
|
|
201
|
+
<strong>${event._user.name}</strong> assigned this ticket to
|
|
202
|
+
<strong>${event.assignee.name}</strong>
|
|
203
|
+
</div>`;
|
|
204
|
+
} else {
|
|
205
|
+
return html`<div>
|
|
206
|
+
<strong>${event._user.name}</strong> unassigned this ticket
|
|
207
|
+
</div>`;
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
if (event.assignee) {
|
|
211
|
+
return html`<div>
|
|
212
|
+
This ticket was assigned to <strong>${event.assignee.name}</strong>
|
|
213
|
+
</div>`;
|
|
214
|
+
} else {
|
|
215
|
+
return html`<div>This ticket was unassigned</div>`;
|
|
216
|
+
}
|
|
157
217
|
}
|
|
158
|
-
return `A **[ticket](/ticket/all/closed/${event.ticket.uuid}/)** was **${action}**`;
|
|
159
218
|
};
|
|
160
219
|
|
|
161
|
-
export const renderTicketAssigned = (event: TicketEvent):
|
|
220
|
+
export const renderTicketAssigned = (event: TicketEvent): TemplateResult => {
|
|
162
221
|
return event.assignee
|
|
163
222
|
? event.assignee.id === event.created_by.id
|
|
164
|
-
?
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
223
|
+
? html`<div>
|
|
224
|
+
<strong>${getDisplayName(event.created_by)}</strong> took this ticket
|
|
225
|
+
</div>`
|
|
226
|
+
: html`<div>
|
|
227
|
+
${getDisplayName(event.created_by)} assigned this ticket to
|
|
228
|
+
<strong>${getDisplayName(event.assignee)}</strong>
|
|
229
|
+
</div>`
|
|
230
|
+
: html`<div>
|
|
231
|
+
<strong>${getDisplayName(event.created_by)}</strong> unassigned this
|
|
232
|
+
ticket
|
|
233
|
+
</div>`;
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
export const renderTicketOpened = (event: TicketEvent): TemplateResult => {
|
|
237
|
+
return html`<div>${event.ticket.topic.name} ticket was opened</div>`;
|
|
169
238
|
};
|
|
170
239
|
|
|
171
|
-
export const renderContactGroupsEvent = (
|
|
240
|
+
export const renderContactGroupsEvent = (
|
|
241
|
+
event: ContactGroupsEvent
|
|
242
|
+
): TemplateResult => {
|
|
172
243
|
const groupsEvent = event as ContactGroupsEvent;
|
|
173
244
|
if (groupsEvent.groups_added) {
|
|
174
245
|
return renderInfoList(
|
|
@@ -185,48 +256,50 @@ export const renderContactGroupsEvent = (event: ContactGroupsEvent): string => {
|
|
|
185
256
|
}
|
|
186
257
|
};
|
|
187
258
|
|
|
188
|
-
export const renderTicketOpened = (event: TicketEvent): string => {
|
|
189
|
-
return `${event.ticket.topic.name} ticket was opened`;
|
|
190
|
-
};
|
|
191
|
-
|
|
192
259
|
export const renderAirtimeTransferredEvent = (
|
|
193
260
|
event: AirtimeTransferredEvent
|
|
194
|
-
):
|
|
261
|
+
): TemplateResult => {
|
|
195
262
|
if (parseFloat(event.amount) === 0) {
|
|
196
|
-
return
|
|
263
|
+
return html`<div>Airtime transfer failed</div>`;
|
|
197
264
|
}
|
|
198
|
-
return
|
|
265
|
+
return html`<div>
|
|
266
|
+
Transferred <strong>${event.amount}</strong> ${event.currency} of airtime
|
|
267
|
+
</div>`;
|
|
199
268
|
};
|
|
200
269
|
|
|
201
270
|
export const renderContactLanguageChangedEvent = (
|
|
202
271
|
event: ContactLanguageChangedEvent
|
|
203
|
-
):
|
|
204
|
-
return
|
|
272
|
+
): TemplateResult => {
|
|
273
|
+
return html`<div>
|
|
274
|
+
Language updated to <strong>${event.language}</strong>
|
|
275
|
+
</div>`;
|
|
205
276
|
};
|
|
206
277
|
|
|
207
278
|
export const renderContactStatusChangedEvent = (
|
|
208
279
|
event: ContactStatusChangedEvent
|
|
209
|
-
):
|
|
210
|
-
return
|
|
280
|
+
): TemplateResult => {
|
|
281
|
+
return html`<div>Status updated to <strong>${event.status}</strong></div>`;
|
|
211
282
|
};
|
|
212
283
|
|
|
213
|
-
export const renderCallEvent = (event: CallEvent):
|
|
284
|
+
export const renderCallEvent = (event: CallEvent): TemplateResult => {
|
|
214
285
|
if (event.type === Events.CALL_CREATED) {
|
|
215
|
-
return
|
|
286
|
+
return html`<div>Call started</div>`;
|
|
216
287
|
} else if (event.type === Events.CALL_MISSED) {
|
|
217
|
-
return
|
|
288
|
+
return html`<div>Call missed</div>`;
|
|
218
289
|
} else if (event.type === Events.CALL_RECEIVED) {
|
|
219
|
-
return
|
|
290
|
+
return html`<div>Call answered</div>`;
|
|
220
291
|
}
|
|
221
292
|
};
|
|
222
293
|
|
|
223
|
-
export const renderOptInEvent = (event: OptInEvent):
|
|
294
|
+
export const renderOptInEvent = (event: OptInEvent): TemplateResult => {
|
|
224
295
|
if (event.type === Events.OPTIN_REQUESTED) {
|
|
225
|
-
return
|
|
296
|
+
return html`<div>
|
|
297
|
+
Requested opt-in for <strong>${event.optin.name}</strong>
|
|
298
|
+
</div>`;
|
|
226
299
|
} else if (event.type === Events.OPTIN_STARTED) {
|
|
227
|
-
return
|
|
300
|
+
return html`<div>Opted in to <strong>${event.optin.name}</strong></div>`;
|
|
228
301
|
} else if (event.type === Events.OPTIN_STOPPED) {
|
|
229
|
-
return
|
|
302
|
+
return html`<div>Opted out of <strong>${event.optin.name}</strong></div>`;
|
|
230
303
|
}
|
|
231
304
|
};
|
|
232
305
|
|
|
@@ -703,6 +776,12 @@ export class ContactChat extends ContactStoreElement {
|
|
|
703
776
|
text: renderTicketAssigned(event as TicketEvent)
|
|
704
777
|
};
|
|
705
778
|
break;
|
|
779
|
+
case Events.TICKET_ASSIGNEE_CHANGED:
|
|
780
|
+
message = {
|
|
781
|
+
type: MessageType.Inline,
|
|
782
|
+
text: renderTicketAssigneeChanged(event as TicketEvent)
|
|
783
|
+
};
|
|
784
|
+
break;
|
|
706
785
|
case Events.TICKET_CLOSED:
|
|
707
786
|
message = {
|
|
708
787
|
type: MessageType.Inline,
|
|
@@ -724,7 +803,10 @@ export class ContactChat extends ContactStoreElement {
|
|
|
724
803
|
case Events.TICKET_TOPIC_CHANGED:
|
|
725
804
|
message = {
|
|
726
805
|
type: MessageType.Inline,
|
|
727
|
-
text:
|
|
806
|
+
text: html`<div>
|
|
807
|
+
Topic changed to
|
|
808
|
+
<strong>${(event as TicketEvent).topic.name}</strong>
|
|
809
|
+
</div>`
|
|
728
810
|
};
|
|
729
811
|
break;
|
|
730
812
|
case Events.CHANNEL_EVENT: // deprecated
|
|
@@ -741,6 +823,10 @@ export class ContactChat extends ContactStoreElement {
|
|
|
741
823
|
console.error('Unknown event type', event);
|
|
742
824
|
}
|
|
743
825
|
|
|
826
|
+
if (!message.id) {
|
|
827
|
+
message.id = event.uuid || event.type + '@' + event.created_on;
|
|
828
|
+
}
|
|
829
|
+
|
|
744
830
|
return message;
|
|
745
831
|
}
|
|
746
832
|
|
|
@@ -94,6 +94,13 @@ export class ContactDetails extends ContactStoreElement {
|
|
|
94
94
|
disabled
|
|
95
95
|
></temba-contact-field>`;
|
|
96
96
|
})}
|
|
97
|
+
${this.data.ref
|
|
98
|
+
? html`<temba-contact-field
|
|
99
|
+
name="Ref"
|
|
100
|
+
value=${this.data.ref}
|
|
101
|
+
disabled
|
|
102
|
+
></temba-contact-field>`
|
|
103
|
+
: null}
|
|
97
104
|
|
|
98
105
|
<temba-contact-field
|
|
99
106
|
name="Status"
|
|
@@ -22,7 +22,7 @@ export class ContactNameFetch extends ContactStoreElement {
|
|
|
22
22
|
public render(): TemplateResult {
|
|
23
23
|
if (this.data) {
|
|
24
24
|
return html` <temba-contact-name
|
|
25
|
-
name=${this.data.name || this.data.
|
|
25
|
+
name=${this.data.name || this.data.ref}
|
|
26
26
|
urn=${this.data.urns.length > 0 ? this.data.urns[0] : null}
|
|
27
27
|
></temba-contact-name>
|
|
28
28
|
<slot></slot>`;
|
package/static/api/labels.json
CHANGED
package/test/NodeHelper.ts
CHANGED
|
@@ -92,46 +92,44 @@ export class NodeTest<T extends Node> {
|
|
|
92
92
|
* 3. Simulates save and validates round-trip conversion
|
|
93
93
|
*/
|
|
94
94
|
async testNode(node: T, nodeUI: any, testName: string) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
});
|
|
95
|
+
// Step 1: Render node in flow node
|
|
96
|
+
const flowNode = await this.renderNode(node, nodeUI);
|
|
97
|
+
|
|
98
|
+
// For execute_actions nodes, check for .body, for router nodes check for .router or .categories
|
|
99
|
+
const hasContent =
|
|
100
|
+
flowNode.querySelector('.body') ||
|
|
101
|
+
flowNode.querySelector('.router') ||
|
|
102
|
+
flowNode.querySelector('.categories') ||
|
|
103
|
+
flowNode.querySelector('.action') ||
|
|
104
|
+
flowNode.textContent?.trim();
|
|
105
|
+
|
|
106
|
+
expect(hasContent).to.exist;
|
|
107
|
+
await assertScreenshot(
|
|
108
|
+
`nodes/${this.nodeName}/render/${testName}`,
|
|
109
|
+
getClip(flowNode)
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// Step 2: Open node editor
|
|
113
|
+
const nodeEditor = await this.openNodeEditor(node, nodeUI);
|
|
114
|
+
await this.assertDialogScreenshot(
|
|
115
|
+
nodeEditor,
|
|
116
|
+
`nodes/${this.nodeName}/editor/${testName}`
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
// Step 3: Test round-trip conversion (simulates save workflow)
|
|
120
|
+
if (this.nodeConfig.toFormData && this.nodeConfig.fromFormData) {
|
|
121
|
+
const formData = this.nodeConfig.toFormData(node);
|
|
122
|
+
const convertedNode = this.nodeConfig.fromFormData(formData, node) as T;
|
|
123
|
+
|
|
124
|
+
// Validate the round trip worked
|
|
125
|
+
expect(convertedNode.uuid).to.equal(node.uuid);
|
|
126
|
+
|
|
127
|
+
// Validate the converted node has expected structure
|
|
128
|
+
expect(convertedNode).to.have.property('actions');
|
|
129
|
+
expect(convertedNode).to.have.property('exits');
|
|
130
|
+
|
|
131
|
+
expect(convertedNode).to.deep.equal(node);
|
|
132
|
+
}
|
|
135
133
|
}
|
|
136
134
|
|
|
137
135
|
/**
|
|
@@ -196,37 +196,48 @@ describe('split_by_llm node config', () => {
|
|
|
196
196
|
position: { left: 50, top: 50 }
|
|
197
197
|
};
|
|
198
198
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
'
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
);
|
|
199
|
+
it('renders translation task', async () => {
|
|
200
|
+
await helper.testNode(
|
|
201
|
+
createTestNode(
|
|
202
|
+
{ uuid: 'gpt-4', name: 'GPT 4.1' },
|
|
203
|
+
'Translate to French'
|
|
204
|
+
),
|
|
205
|
+
nodeUI,
|
|
206
|
+
'translation-task'
|
|
207
|
+
);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('renders sentiment analysis', async () => {
|
|
211
|
+
await helper.testNode(
|
|
212
|
+
createTestNode(
|
|
213
|
+
{ uuid: 'gpt-5', name: 'GPT 5' },
|
|
214
|
+
'Analyze the sentiment of the following message and classify it as positive, negative, or neutral. Provide a brief explanation for your classification.'
|
|
215
|
+
),
|
|
216
|
+
nodeUI,
|
|
217
|
+
'sentiment-analysis'
|
|
218
|
+
);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('renders summarization', async () => {
|
|
222
|
+
await helper.testNode(
|
|
223
|
+
createTestNode(
|
|
224
|
+
{ uuid: 'gpt-4', name: 'GPT 4.1' },
|
|
225
|
+
'Summarize the key points from the conversation above in bullet format.'
|
|
226
|
+
),
|
|
227
|
+
nodeUI,
|
|
228
|
+
'summarization'
|
|
229
|
+
);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('renders information extraction', async () => {
|
|
233
|
+
await helper.testNode(
|
|
234
|
+
createTestNode(
|
|
235
|
+
{ uuid: 'gpt-5', name: 'GPT 5' },
|
|
236
|
+
'Extract any contact information (phone numbers, email addresses) from the text and format them as a JSON object.'
|
|
237
|
+
),
|
|
238
|
+
nodeUI,
|
|
239
|
+
'information-extraction'
|
|
240
|
+
);
|
|
241
|
+
});
|
|
231
242
|
});
|
|
232
243
|
});
|