@nyaruka/temba-components 0.130.1 → 0.130.3
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 +34 -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 +787 -659
- 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 +101 -44
- 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/src/webchat/index.js +0 -11
- package/out-tsc/src/webchat/index.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 -6
- 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 +128 -67
- package/src/live/ContactDetails.ts +7 -0
- package/src/live/ContactNameFetch.ts +1 -1
- package/src/webchat/index.ts +0 -16
- 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/contacts/history.json +4 -7
- 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
|
@@ -37,7 +37,6 @@ import {
|
|
|
37
37
|
URNsChangedEvent
|
|
38
38
|
} from '../events';
|
|
39
39
|
import { Chat, ChatEvent, MessageType } from '../display/Chat';
|
|
40
|
-
import { getUserDisplay } from '../webchat';
|
|
41
40
|
import { DEFAULT_AVATAR } from '../webchat/assets';
|
|
42
41
|
import { UserSelect } from '../form/select/UserSelect';
|
|
43
42
|
import { Select } from '../form/select/Select';
|
|
@@ -73,7 +72,7 @@ export enum Events {
|
|
|
73
72
|
OPTIN_STOPPED = 'optin_stopped',
|
|
74
73
|
RUN_ENDED = 'run_ended',
|
|
75
74
|
RUN_STARTED = 'run_started',
|
|
76
|
-
|
|
75
|
+
TICKET_ASSIGNEE_CHANGED = 'ticket_assignee_changed',
|
|
77
76
|
TICKET_CLOSED = 'ticket_closed',
|
|
78
77
|
TICKET_NOTE_ADDED = 'ticket_note_added',
|
|
79
78
|
TICKET_OPENED = 'ticket_opened',
|
|
@@ -81,32 +80,43 @@ export enum Events {
|
|
|
81
80
|
TICKET_TOPIC_CHANGED = 'ticket_topic_changed',
|
|
82
81
|
|
|
83
82
|
// deprecated
|
|
84
|
-
CHANNEL_EVENT = 'channel_event'
|
|
83
|
+
CHANNEL_EVENT = 'channel_event',
|
|
84
|
+
TICKET_ASSIGNED = 'ticket_assigned'
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
const renderInfoList = (
|
|
87
|
+
const renderInfoList = (
|
|
88
|
+
singular: string,
|
|
89
|
+
plural: string,
|
|
90
|
+
items: any[]
|
|
91
|
+
): TemplateResult => {
|
|
88
92
|
if (items.length === 1) {
|
|
89
|
-
return
|
|
93
|
+
return html`<div>${singular} <strong>${items[0].name}</strong></div>`;
|
|
90
94
|
} else {
|
|
91
|
-
const list = items.map((item) =>
|
|
95
|
+
const list = items.map((item) => item.name);
|
|
92
96
|
if (list.length === 2) {
|
|
93
|
-
return
|
|
97
|
+
return html`<div>
|
|
98
|
+
${plural} <strong>${list[0]}</strong> and <strong>${list[1]}</strong>
|
|
99
|
+
</div>`;
|
|
94
100
|
} else {
|
|
95
101
|
const last = list.pop();
|
|
96
|
-
|
|
102
|
+
const middle = list.map(
|
|
103
|
+
(name, index) =>
|
|
104
|
+
html`<strong>${name}</strong>${index < list.length - 1 ? ', ' : ''}`
|
|
105
|
+
);
|
|
106
|
+
return html`<div>${plural} ${middle}, and <strong>${last}</strong></div>`;
|
|
97
107
|
}
|
|
98
108
|
}
|
|
99
109
|
};
|
|
100
110
|
|
|
101
|
-
const renderChannelEvent = (event: ChannelEvent):
|
|
111
|
+
const renderChannelEvent = (event: ChannelEvent): TemplateResult => {
|
|
102
112
|
if (event.channel_event_type === 'welcome_message') {
|
|
103
|
-
return
|
|
113
|
+
return html`<div>Welcome message sent</div>`;
|
|
104
114
|
} else if (event.event.type === 'stop_contact') {
|
|
105
|
-
return
|
|
115
|
+
return html`<div>Stopped</div>`;
|
|
106
116
|
}
|
|
107
117
|
};
|
|
108
118
|
|
|
109
|
-
const renderRunEvent = (event: RunEvent):
|
|
119
|
+
const renderRunEvent = (event: RunEvent): TemplateResult => {
|
|
110
120
|
let verb = 'Started';
|
|
111
121
|
if (event.type === Events.RUN_ENDED) {
|
|
112
122
|
if (event.status === 'completed') {
|
|
@@ -118,57 +128,98 @@ const renderRunEvent = (event: RunEvent): string => {
|
|
|
118
128
|
}
|
|
119
129
|
}
|
|
120
130
|
|
|
121
|
-
return
|
|
131
|
+
return html`<div>
|
|
132
|
+
${verb}
|
|
133
|
+
<a href="/flow/editor/${event.flow.uuid}/"
|
|
134
|
+
><strong>${event.flow.name}</strong></a
|
|
135
|
+
>
|
|
136
|
+
</div>`;
|
|
122
137
|
};
|
|
123
138
|
|
|
124
|
-
const renderChatStartedEvent = (event: ChatStartedEvent):
|
|
139
|
+
const renderChatStartedEvent = (event: ChatStartedEvent): TemplateResult => {
|
|
125
140
|
if (event.params) {
|
|
126
|
-
return
|
|
141
|
+
return html`<div>Chat referral</div>`;
|
|
127
142
|
} else {
|
|
128
|
-
return
|
|
143
|
+
return html`<div>Chat started</div>`;
|
|
129
144
|
}
|
|
130
145
|
};
|
|
131
146
|
|
|
132
|
-
const renderUpdateEvent = (event: UpdateFieldEvent):
|
|
147
|
+
const renderUpdateEvent = (event: UpdateFieldEvent): TemplateResult => {
|
|
133
148
|
return event.value
|
|
134
|
-
?
|
|
135
|
-
|
|
149
|
+
? html`<div>
|
|
150
|
+
Updated <strong>${event.field.name}</strong> to
|
|
151
|
+
<strong>${event.value.text}</strong>
|
|
152
|
+
</div>`
|
|
153
|
+
: html`<div>Cleared <strong>${event.field.name}</strong></div>`;
|
|
136
154
|
};
|
|
137
155
|
|
|
138
|
-
const renderNameChanged = (event: NameChangedEvent):
|
|
139
|
-
return
|
|
156
|
+
const renderNameChanged = (event: NameChangedEvent): TemplateResult => {
|
|
157
|
+
return html`<div>
|
|
158
|
+
Updated <strong>name</strong> to <strong>${event.name}</strong>
|
|
159
|
+
</div>`;
|
|
140
160
|
};
|
|
141
161
|
|
|
142
|
-
const renderContactURNsChanged = (event: URNsChangedEvent):
|
|
143
|
-
return
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
162
|
+
const renderContactURNsChanged = (event: URNsChangedEvent): TemplateResult => {
|
|
163
|
+
return html`<div>
|
|
164
|
+
Updated <strong>URNs</strong> to
|
|
165
|
+
${oxfordFn(
|
|
166
|
+
event.urns,
|
|
167
|
+
(urn: string) => html`<strong>${urn.split(':')[1].split('?')[0]}</strong>`
|
|
168
|
+
)}
|
|
169
|
+
</div>`;
|
|
147
170
|
};
|
|
148
171
|
|
|
149
172
|
export const renderTicketAction = (
|
|
150
173
|
event: TicketEvent,
|
|
151
174
|
action: string
|
|
152
|
-
):
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
175
|
+
): TemplateResult => {
|
|
176
|
+
const ticketUUID = event.ticket?.uuid || event.ticket_uuid;
|
|
177
|
+
|
|
178
|
+
if (event._user) {
|
|
179
|
+
return html`<div>
|
|
180
|
+
<strong>${event._user.name}</strong> ${action} a
|
|
181
|
+
<strong><a href="/ticket/all/closed/${ticketUUID}/">ticket</a></strong>
|
|
182
|
+
</div>`;
|
|
157
183
|
}
|
|
158
|
-
return
|
|
184
|
+
return html`<div>
|
|
185
|
+
A
|
|
186
|
+
<strong><a href="/ticket/all/closed/${ticketUUID}/">ticket</a></strong> was
|
|
187
|
+
<strong>${action}</strong>
|
|
188
|
+
</div>`;
|
|
159
189
|
};
|
|
160
190
|
|
|
161
|
-
export const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
191
|
+
export const renderTicketAssigneeChanged = (
|
|
192
|
+
event: TicketEvent
|
|
193
|
+
): TemplateResult => {
|
|
194
|
+
if (event._user) {
|
|
195
|
+
if (event.assignee) {
|
|
196
|
+
return html`<div>
|
|
197
|
+
<strong>${event._user.name}</strong> assigned this ticket to
|
|
198
|
+
<strong>${event.assignee.name}</strong>
|
|
199
|
+
</div>`;
|
|
200
|
+
} else {
|
|
201
|
+
return html`<div>
|
|
202
|
+
<strong>${event._user.name}</strong> unassigned this ticket
|
|
203
|
+
</div>`;
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
if (event.assignee) {
|
|
207
|
+
return html`<div>
|
|
208
|
+
This ticket was assigned to <strong>${event.assignee.name}</strong>
|
|
209
|
+
</div>`;
|
|
210
|
+
} else {
|
|
211
|
+
return html`<div>This ticket was unassigned</div>`;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
169
214
|
};
|
|
170
215
|
|
|
171
|
-
export const
|
|
216
|
+
export const renderTicketOpened = (event: TicketEvent): TemplateResult => {
|
|
217
|
+
return html`<div>${event.ticket.topic.name} ticket was opened</div>`;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
export const renderContactGroupsEvent = (
|
|
221
|
+
event: ContactGroupsEvent
|
|
222
|
+
): TemplateResult => {
|
|
172
223
|
const groupsEvent = event as ContactGroupsEvent;
|
|
173
224
|
if (groupsEvent.groups_added) {
|
|
174
225
|
return renderInfoList(
|
|
@@ -185,48 +236,50 @@ export const renderContactGroupsEvent = (event: ContactGroupsEvent): string => {
|
|
|
185
236
|
}
|
|
186
237
|
};
|
|
187
238
|
|
|
188
|
-
export const renderTicketOpened = (event: TicketEvent): string => {
|
|
189
|
-
return `${event.ticket.topic.name} ticket was opened`;
|
|
190
|
-
};
|
|
191
|
-
|
|
192
239
|
export const renderAirtimeTransferredEvent = (
|
|
193
240
|
event: AirtimeTransferredEvent
|
|
194
|
-
):
|
|
241
|
+
): TemplateResult => {
|
|
195
242
|
if (parseFloat(event.amount) === 0) {
|
|
196
|
-
return
|
|
243
|
+
return html`<div>Airtime transfer failed</div>`;
|
|
197
244
|
}
|
|
198
|
-
return
|
|
245
|
+
return html`<div>
|
|
246
|
+
Transferred <strong>${event.amount}</strong> ${event.currency} of airtime
|
|
247
|
+
</div>`;
|
|
199
248
|
};
|
|
200
249
|
|
|
201
250
|
export const renderContactLanguageChangedEvent = (
|
|
202
251
|
event: ContactLanguageChangedEvent
|
|
203
|
-
):
|
|
204
|
-
return
|
|
252
|
+
): TemplateResult => {
|
|
253
|
+
return html`<div>
|
|
254
|
+
Language updated to <strong>${event.language}</strong>
|
|
255
|
+
</div>`;
|
|
205
256
|
};
|
|
206
257
|
|
|
207
258
|
export const renderContactStatusChangedEvent = (
|
|
208
259
|
event: ContactStatusChangedEvent
|
|
209
|
-
):
|
|
210
|
-
return
|
|
260
|
+
): TemplateResult => {
|
|
261
|
+
return html`<div>Status updated to <strong>${event.status}</strong></div>`;
|
|
211
262
|
};
|
|
212
263
|
|
|
213
|
-
export const renderCallEvent = (event: CallEvent):
|
|
264
|
+
export const renderCallEvent = (event: CallEvent): TemplateResult => {
|
|
214
265
|
if (event.type === Events.CALL_CREATED) {
|
|
215
|
-
return
|
|
266
|
+
return html`<div>Call started</div>`;
|
|
216
267
|
} else if (event.type === Events.CALL_MISSED) {
|
|
217
|
-
return
|
|
268
|
+
return html`<div>Call missed</div>`;
|
|
218
269
|
} else if (event.type === Events.CALL_RECEIVED) {
|
|
219
|
-
return
|
|
270
|
+
return html`<div>Call answered</div>`;
|
|
220
271
|
}
|
|
221
272
|
};
|
|
222
273
|
|
|
223
|
-
export const renderOptInEvent = (event: OptInEvent):
|
|
274
|
+
export const renderOptInEvent = (event: OptInEvent): TemplateResult => {
|
|
224
275
|
if (event.type === Events.OPTIN_REQUESTED) {
|
|
225
|
-
return
|
|
276
|
+
return html`<div>
|
|
277
|
+
Requested opt-in for <strong>${event.optin.name}</strong>
|
|
278
|
+
</div>`;
|
|
226
279
|
} else if (event.type === Events.OPTIN_STARTED) {
|
|
227
|
-
return
|
|
280
|
+
return html`<div>Opted in to <strong>${event.optin.name}</strong></div>`;
|
|
228
281
|
} else if (event.type === Events.OPTIN_STOPPED) {
|
|
229
|
-
return
|
|
282
|
+
return html`<div>Opted out of <strong>${event.optin.name}</strong></div>`;
|
|
230
283
|
}
|
|
231
284
|
};
|
|
232
285
|
|
|
@@ -697,10 +750,10 @@ export class ContactChat extends ContactStoreElement {
|
|
|
697
750
|
text: renderRunEvent(event as RunEvent)
|
|
698
751
|
};
|
|
699
752
|
break;
|
|
700
|
-
case Events.
|
|
753
|
+
case Events.TICKET_ASSIGNEE_CHANGED:
|
|
701
754
|
message = {
|
|
702
755
|
type: MessageType.Inline,
|
|
703
|
-
text:
|
|
756
|
+
text: renderTicketAssigneeChanged(event as TicketEvent)
|
|
704
757
|
};
|
|
705
758
|
break;
|
|
706
759
|
case Events.TICKET_CLOSED:
|
|
@@ -724,7 +777,10 @@ export class ContactChat extends ContactStoreElement {
|
|
|
724
777
|
case Events.TICKET_TOPIC_CHANGED:
|
|
725
778
|
message = {
|
|
726
779
|
type: MessageType.Inline,
|
|
727
|
-
text:
|
|
780
|
+
text: html`<div>
|
|
781
|
+
Topic changed to
|
|
782
|
+
<strong>${(event as TicketEvent).topic.name}</strong>
|
|
783
|
+
</div>`
|
|
728
784
|
};
|
|
729
785
|
break;
|
|
730
786
|
case Events.CHANNEL_EVENT: // deprecated
|
|
@@ -741,23 +797,28 @@ export class ContactChat extends ContactStoreElement {
|
|
|
741
797
|
console.error('Unknown event type', event);
|
|
742
798
|
}
|
|
743
799
|
|
|
800
|
+
if (!message.id) {
|
|
801
|
+
message.id = event.uuid || event.type + '@' + event.created_on;
|
|
802
|
+
}
|
|
803
|
+
|
|
744
804
|
return message;
|
|
745
805
|
}
|
|
746
806
|
|
|
747
807
|
private getUserForEvent(event: MsgEvent | TicketEvent) {
|
|
748
|
-
let user = null;
|
|
749
808
|
if (event.type === 'msg_received') {
|
|
750
|
-
|
|
809
|
+
return {
|
|
751
810
|
name: this.currentContact.name
|
|
752
811
|
};
|
|
812
|
+
} else if (event._user) {
|
|
813
|
+
return event._user;
|
|
753
814
|
} else if (event.created_by) {
|
|
754
|
-
|
|
815
|
+
return {
|
|
755
816
|
email: event.created_by.email,
|
|
756
817
|
name: `${event.created_by.first_name} ${event.created_by.last_name}`.trim(),
|
|
757
818
|
avatar: event.created_by.avatar
|
|
758
819
|
};
|
|
759
820
|
}
|
|
760
|
-
return
|
|
821
|
+
return null;
|
|
761
822
|
}
|
|
762
823
|
|
|
763
824
|
private createMessages(page: ContactHistoryPage): ChatEvent[] {
|
|
@@ -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/src/webchat/index.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { User } from '../interfaces';
|
|
2
|
-
|
|
3
1
|
export const SVG_FINGERPRINT = 'febafb41c2fd60efa2bdaead993c7087';
|
|
4
2
|
|
|
5
3
|
// webchat spritesheet
|
|
@@ -12,17 +10,3 @@ export enum WebChatIcon {
|
|
|
12
10
|
attachment_location = 'marker-pin-01',
|
|
13
11
|
attachment_video = 'video-recorder'
|
|
14
12
|
}
|
|
15
|
-
|
|
16
|
-
export const getUserDisplay = (user: User) => {
|
|
17
|
-
if (user) {
|
|
18
|
-
if (user.first_name && user.last_name) {
|
|
19
|
-
return `${user.first_name} ${user.last_name}`;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (user.first_name) {
|
|
23
|
-
return user.first_name;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return user.email;
|
|
27
|
-
}
|
|
28
|
-
};
|
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
|
});
|