@nyaruka/temba-components 0.129.10 → 0.129.11
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 +13 -0
- package/demo/data/flows/sample-flow.json +96 -56
- package/dist/temba-components.js +278 -268
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/events.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +9 -6
- package/out-tsc/src/flow/Editor.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 +1 -1
- package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_language.js +1 -1
- package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_status.js +1 -1
- package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
- package/out-tsc/src/flow/config.js +2 -8
- package/out-tsc/src/flow/config.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_llm.js +101 -0
- package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +4 -89
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_subflow.js +123 -3
- package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_ticket.js +115 -13
- package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_webhook.js +160 -12
- package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
- package/out-tsc/src/live/ContactChat.js +48 -66
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/src/utils.js +115 -0
- package/out-tsc/src/utils.js.map +1 -1
- package/out-tsc/test/nodes/split_by_llm.test.js +174 -0
- package/out-tsc/test/nodes/split_by_llm.test.js.map +1 -0
- 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/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/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/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/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_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-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/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/compose/attachments-tab.png +0 -0
- package/screenshots/truth/compose/attachments-with-files.png +0 -0
- package/screenshots/truth/compose/intial-text.png +0 -0
- package/screenshots/truth/compose/no-counter.png +0 -0
- package/screenshots/truth/compose/wraps-text-and-spaces.png +0 -0
- package/screenshots/truth/compose/wraps-text-and-url.png +0 -0
- package/screenshots/truth/compose/wraps-text-no-spaces.png +0 -0
- package/screenshots/truth/contacts/chat-failure.png +0 -0
- package/screenshots/truth/contacts/chat-for-active-contact.png +0 -0
- package/screenshots/truth/contacts/chat-sends-attachments-only.png +0 -0
- package/screenshots/truth/contacts/chat-sends-text-and-attachments.png +0 -0
- package/screenshots/truth/contacts/chat-sends-text-only.png +0 -0
- package/screenshots/truth/counter/summary.png +0 -0
- package/screenshots/truth/counter/text.png +0 -0
- package/screenshots/truth/counter/unicode-variables.png +0 -0
- package/screenshots/truth/counter/unicode.png +0 -0
- package/screenshots/truth/counter/variable.png +0 -0
- package/screenshots/truth/datepicker/date-truncated-time.png +0 -0
- package/screenshots/truth/datepicker/date.png +0 -0
- package/screenshots/truth/datepicker/initial-timezone.png +0 -0
- package/screenshots/truth/datepicker/range-picker-editing-start.png +0 -0
- package/screenshots/truth/datepicker/updated-keyboard-date.png +0 -0
- package/screenshots/truth/dialog/focused.png +0 -0
- package/screenshots/truth/dropdown/right-edge-collision.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/list/fields-dragging.png +0 -0
- package/screenshots/truth/list/fields-filtered.png +0 -0
- package/screenshots/truth/list/fields-hovered.png +0 -0
- package/screenshots/truth/list/fields.png +0 -0
- package/screenshots/truth/list/items-selected.png +0 -0
- package/screenshots/truth/list/items-updated.png +0 -0
- package/screenshots/truth/list/items.png +0 -0
- package/screenshots/truth/menu/menu-focused-with items.png +0 -0
- package/screenshots/truth/menu/menu-refresh-1.png +0 -0
- package/screenshots/truth/menu/menu-refresh-2.png +0 -0
- package/screenshots/truth/menu/menu-submenu.png +0 -0
- package/screenshots/truth/menu/menu-tasks-nextup.png +0 -0
- package/screenshots/truth/menu/menu-tasks.png +0 -0
- package/screenshots/truth/message-editor/autogrow-initial-content.png +0 -0
- package/screenshots/truth/message-editor/default.png +0 -0
- package/screenshots/truth/message-editor/drag-highlight.png +0 -0
- package/screenshots/truth/message-editor/filtered-attachments.png +0 -0
- package/screenshots/truth/message-editor/with-completion.png +0 -0
- package/screenshots/truth/message-editor/with-properties.png +0 -0
- package/screenshots/truth/modax/form.png +0 -0
- package/screenshots/truth/modax/simple.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_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/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_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/omnibox/selected.png +0 -0
- package/screenshots/truth/options/block.png +0 -0
- package/screenshots/truth/run-list/basic.png +0 -0
- package/screenshots/truth/select/disabled-multi-selection.png +0 -0
- package/screenshots/truth/select/disabled-selection.png +0 -0
- package/screenshots/truth/select/disabled.png +0 -0
- package/screenshots/truth/select/embedded.png +0 -0
- package/screenshots/truth/select/empty-options.png +0 -0
- package/screenshots/truth/select/expression-selected.png +0 -0
- package/screenshots/truth/select/expressions.png +0 -0
- package/screenshots/truth/select/functions.png +0 -0
- package/screenshots/truth/select/local-options.png +0 -0
- package/screenshots/truth/select/multi-with-endpoint.png +0 -0
- package/screenshots/truth/select/multiple-initial-values.png +0 -0
- package/screenshots/truth/select/remote-options.png +0 -0
- package/screenshots/truth/select/search-enabled.png +0 -0
- package/screenshots/truth/select/search-multi-no-matches.png +0 -0
- package/screenshots/truth/select/search-selected-focus.png +0 -0
- package/screenshots/truth/select/search-selected.png +0 -0
- package/screenshots/truth/select/search-with-selected.png +0 -0
- package/screenshots/truth/select/searching.png +0 -0
- package/screenshots/truth/select/selected-multi-maxitems-reached.png +0 -0
- package/screenshots/truth/select/selected-multi.png +0 -0
- package/screenshots/truth/select/selected-single.png +0 -0
- package/screenshots/truth/select/selection-clearable.png +0 -0
- package/screenshots/truth/select/static-initial-value.png +0 -0
- package/screenshots/truth/select/static-initial-via-selected.png +0 -0
- package/screenshots/truth/select/truncated-selection.png +0 -0
- package/screenshots/truth/select/with-placeholder.png +0 -0
- package/screenshots/truth/select/without-placeholder.png +0 -0
- package/screenshots/truth/slider/update-slider-on-circle-dragged.png +0 -0
- package/screenshots/truth/templates/default.png +0 -0
- package/screenshots/truth/templates/unapproved.png +0 -0
- package/screenshots/truth/textinput/autogrow-initial.png +0 -0
- package/screenshots/truth/textinput/input-disabled.png +0 -0
- package/screenshots/truth/textinput/input-focused.png +0 -0
- package/screenshots/truth/textinput/input-form.png +0 -0
- package/screenshots/truth/textinput/input-inserted.png +0 -0
- package/screenshots/truth/textinput/input-placeholder.png +0 -0
- package/screenshots/truth/textinput/input-updated.png +0 -0
- package/screenshots/truth/textinput/input.png +0 -0
- package/screenshots/truth/textinput/textarea-focused.png +0 -0
- package/screenshots/truth/textinput/textarea.png +0 -0
- package/src/events.ts +4 -2
- package/src/flow/Editor.ts +6 -3
- package/src/flow/actions/set_contact_channel.ts +1 -1
- package/src/flow/actions/set_contact_field.ts +1 -1
- package/src/flow/actions/set_contact_language.ts +1 -1
- package/src/flow/actions/set_contact_status.ts +1 -1
- package/src/flow/config.ts +2 -8
- package/src/flow/nodes/split_by_llm.ts +119 -0
- package/src/flow/nodes/split_by_llm_categorize.ts +13 -116
- package/src/flow/nodes/split_by_subflow.ts +153 -3
- package/src/flow/nodes/split_by_ticket.ts +135 -12
- package/src/flow/nodes/split_by_webhook.ts +187 -12
- package/src/live/ContactChat.ts +56 -58
- package/src/store/flow-definition.d.ts +2 -1
- package/src/utils.ts +192 -0
- package/test/nodes/split_by_llm.test.ts +232 -0
- package/test-assets/style.css +36 -234
- package/web-dev-server.config.mjs +26 -0
- package/web-test-runner.config.mjs +1 -1
- package/out-tsc/src/flow/actions/call_llm.js +0 -64
- package/out-tsc/src/flow/actions/call_llm.js.map +0 -1
- package/out-tsc/src/flow/actions/call_webhook.js +0 -131
- package/out-tsc/src/flow/actions/call_webhook.js.map +0 -1
- package/out-tsc/src/flow/actions/enter_flow.js +0 -14
- package/out-tsc/src/flow/actions/enter_flow.js.map +0 -1
- package/out-tsc/src/flow/actions/open_ticket.js +0 -73
- package/out-tsc/src/flow/actions/open_ticket.js.map +0 -1
- package/out-tsc/test/actions/call_llm.test.js +0 -103
- package/out-tsc/test/actions/call_llm.test.js.map +0 -1
- package/src/flow/actions/call_llm.ts +0 -66
- package/src/flow/actions/call_webhook.ts +0 -143
- package/src/flow/actions/enter_flow.ts +0 -15
- package/src/flow/actions/open_ticket.ts +0 -83
- package/test/actions/call_llm.test.ts +0 -137
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { COLORS, NodeConfig } from '../types';
|
|
2
|
-
import { Node } from '../../store/flow-definition';
|
|
3
|
-
import { generateUUID } from '../../utils';
|
|
2
|
+
import { CallLLM, Node } from '../../store/flow-definition';
|
|
3
|
+
import { generateUUID, createMultiCategoryRouter } from '../../utils';
|
|
4
4
|
import { html } from 'lit';
|
|
5
5
|
|
|
6
6
|
export const split_by_llm_categorize: NodeConfig = {
|
|
@@ -92,7 +92,7 @@ export const split_by_llm_categorize: NodeConfig = {
|
|
|
92
92
|
render: (node: Node) => {
|
|
93
93
|
const callLlmAction = node.actions?.find(
|
|
94
94
|
(action) => action.type === 'call_llm'
|
|
95
|
-
) as
|
|
95
|
+
) as CallLLM;
|
|
96
96
|
return html`
|
|
97
97
|
<div class="body">Categorize with ${callLlmAction.llm.name}</div>
|
|
98
98
|
`;
|
|
@@ -135,7 +135,7 @@ export const split_by_llm_categorize: NodeConfig = {
|
|
|
135
135
|
const callLlmUuid = existingCallLlmAction?.uuid || generateUUID();
|
|
136
136
|
|
|
137
137
|
// Create call_llm action (using any type to match the example format)
|
|
138
|
-
const callLlmAction:
|
|
138
|
+
const callLlmAction: CallLLM = {
|
|
139
139
|
type: 'call_llm',
|
|
140
140
|
uuid: callLlmUuid,
|
|
141
141
|
llm: llmSelection
|
|
@@ -147,124 +147,21 @@ export const split_by_llm_categorize: NodeConfig = {
|
|
|
147
147
|
};
|
|
148
148
|
|
|
149
149
|
// Create categories and exits
|
|
150
|
-
const categories = [];
|
|
151
|
-
const exits = [];
|
|
152
|
-
const cases = [];
|
|
153
|
-
|
|
154
|
-
// Get existing categories from original node for UUID preservation
|
|
155
150
|
const existingCategories = originalNode.router?.categories || [];
|
|
156
151
|
const existingExits = originalNode.exits || [];
|
|
157
152
|
const existingCases = originalNode.router?.cases || [];
|
|
158
153
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
(cat) => cat.name === categoryName
|
|
164
|
-
);
|
|
165
|
-
const existingExit = existingCategory
|
|
166
|
-
? existingExits.find((exit) => exit.uuid === existingCategory.exit_uuid)
|
|
167
|
-
: null;
|
|
168
|
-
const existingCase = existingCategory
|
|
169
|
-
? existingCases.find(
|
|
170
|
-
(case_) => case_.category_uuid === existingCategory.uuid
|
|
171
|
-
)
|
|
172
|
-
: null;
|
|
173
|
-
|
|
174
|
-
// Use existing UUIDs if category name hasn't changed, otherwise generate new ones
|
|
175
|
-
const categoryUuid = existingCategory?.uuid || generateUUID();
|
|
176
|
-
const exitUuid = existingExit?.uuid || generateUUID();
|
|
177
|
-
const caseUuid = existingCase?.uuid || generateUUID();
|
|
178
|
-
|
|
179
|
-
categories.push({
|
|
180
|
-
uuid: categoryUuid,
|
|
181
|
-
name: categoryName,
|
|
182
|
-
exit_uuid: exitUuid
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
exits.push({
|
|
186
|
-
uuid: exitUuid,
|
|
187
|
-
destination_uuid: existingExit?.destination_uuid || null
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
cases.push({
|
|
191
|
-
uuid: caseUuid,
|
|
154
|
+
const { router, exits } = createMultiCategoryRouter(
|
|
155
|
+
'@locals._llm_output',
|
|
156
|
+
userCategories,
|
|
157
|
+
(categoryName) => ({
|
|
192
158
|
type: 'has_only_text',
|
|
193
|
-
arguments: [categoryName]
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
// Add "Other" category (default)
|
|
199
|
-
const existingOtherCategory = existingCategories.find(
|
|
200
|
-
(cat) => cat.name === 'Other'
|
|
201
|
-
);
|
|
202
|
-
const existingOtherExit = existingOtherCategory
|
|
203
|
-
? existingExits.find(
|
|
204
|
-
(exit) => exit.uuid === existingOtherCategory.exit_uuid
|
|
205
|
-
)
|
|
206
|
-
: null;
|
|
207
|
-
|
|
208
|
-
const otherCategoryUuid = existingOtherCategory?.uuid || generateUUID();
|
|
209
|
-
const otherExitUuid = existingOtherExit?.uuid || generateUUID();
|
|
210
|
-
|
|
211
|
-
categories.push({
|
|
212
|
-
uuid: otherCategoryUuid,
|
|
213
|
-
name: 'Other',
|
|
214
|
-
exit_uuid: otherExitUuid
|
|
215
|
-
});
|
|
216
|
-
exits.push({
|
|
217
|
-
uuid: otherExitUuid,
|
|
218
|
-
destination_uuid: existingOtherExit?.destination_uuid || null
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
// Add "Failure" category
|
|
222
|
-
const existingFailureCategory = existingCategories.find(
|
|
223
|
-
(cat) => cat.name === 'Failure'
|
|
159
|
+
arguments: [categoryName]
|
|
160
|
+
}),
|
|
161
|
+
existingCategories,
|
|
162
|
+
existingExits,
|
|
163
|
+
existingCases
|
|
224
164
|
);
|
|
225
|
-
const existingFailureExit = existingFailureCategory
|
|
226
|
-
? existingExits.find(
|
|
227
|
-
(exit) => exit.uuid === existingFailureCategory.exit_uuid
|
|
228
|
-
)
|
|
229
|
-
: null;
|
|
230
|
-
const existingFailureCase = existingFailureCategory
|
|
231
|
-
? existingCases.find(
|
|
232
|
-
(case_) =>
|
|
233
|
-
case_.category_uuid === existingFailureCategory.uuid &&
|
|
234
|
-
case_.arguments?.[0] === '<ERROR>'
|
|
235
|
-
)
|
|
236
|
-
: null;
|
|
237
|
-
|
|
238
|
-
const failureCategoryUuid = existingFailureCategory?.uuid || generateUUID();
|
|
239
|
-
const failureExitUuid = existingFailureExit?.uuid || generateUUID();
|
|
240
|
-
const failureCaseUuid = existingFailureCase?.uuid || generateUUID();
|
|
241
|
-
|
|
242
|
-
categories.push({
|
|
243
|
-
uuid: failureCategoryUuid,
|
|
244
|
-
name: 'Failure',
|
|
245
|
-
exit_uuid: failureExitUuid
|
|
246
|
-
});
|
|
247
|
-
exits.push({
|
|
248
|
-
uuid: failureExitUuid,
|
|
249
|
-
destination_uuid: existingFailureExit?.destination_uuid || null
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
// Add failure case for <ERROR>
|
|
253
|
-
cases.push({
|
|
254
|
-
uuid: failureCaseUuid,
|
|
255
|
-
type: 'has_only_text',
|
|
256
|
-
arguments: ['<ERROR>'],
|
|
257
|
-
category_uuid: failureCategoryUuid
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
// Create the router
|
|
261
|
-
const router = {
|
|
262
|
-
type: 'switch' as const,
|
|
263
|
-
categories: categories,
|
|
264
|
-
default_category_uuid: otherCategoryUuid,
|
|
265
|
-
operand: '@locals._llm_output',
|
|
266
|
-
cases: cases
|
|
267
|
-
};
|
|
268
165
|
|
|
269
166
|
// Return the complete node
|
|
270
167
|
return {
|
|
@@ -1,9 +1,159 @@
|
|
|
1
|
-
import { enter_flow } from '../actions/enter_flow';
|
|
2
1
|
import { COLORS, NodeConfig } from '../types';
|
|
2
|
+
import { Node } from '../../store/flow-definition';
|
|
3
|
+
import { generateUUID } from '../../utils';
|
|
4
|
+
import { html } from 'lit';
|
|
3
5
|
|
|
4
6
|
export const split_by_subflow: NodeConfig = {
|
|
5
7
|
type: 'split_by_subflow',
|
|
6
|
-
name: '
|
|
8
|
+
name: 'Enter a Flow',
|
|
7
9
|
color: COLORS.execute,
|
|
8
|
-
|
|
10
|
+
form: {
|
|
11
|
+
flow: {
|
|
12
|
+
type: 'select',
|
|
13
|
+
required: true,
|
|
14
|
+
placeholder: 'Select a flow...',
|
|
15
|
+
helpText:
|
|
16
|
+
'Once the subflow is complete or expires, the contact will return here',
|
|
17
|
+
endpoint: '/api/v2/flows.json',
|
|
18
|
+
valueKey: 'uuid',
|
|
19
|
+
nameKey: 'name'
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
layout: ['flow'],
|
|
23
|
+
render: (node: Node) => {
|
|
24
|
+
const enterFlowAction = node.actions?.find(
|
|
25
|
+
(action) => action.type === 'enter_flow'
|
|
26
|
+
) as any;
|
|
27
|
+
return html`
|
|
28
|
+
<div class="body">
|
|
29
|
+
${enterFlowAction?.flow?.name || 'Configure subflow'}
|
|
30
|
+
</div>
|
|
31
|
+
`;
|
|
32
|
+
},
|
|
33
|
+
toFormData: (node: Node) => {
|
|
34
|
+
// Extract data from the existing node structure
|
|
35
|
+
const enterFlowAction = node.actions?.find(
|
|
36
|
+
(action) => action.type === 'enter_flow'
|
|
37
|
+
) as any;
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
uuid: node.uuid,
|
|
41
|
+
flow: enterFlowAction?.flow
|
|
42
|
+
? [{ uuid: enterFlowAction.flow.uuid, name: enterFlowAction.flow.name }]
|
|
43
|
+
: []
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
fromFormData: (formData: any, originalNode: Node): Node => {
|
|
47
|
+
// Get flow selection
|
|
48
|
+
const flowSelection =
|
|
49
|
+
Array.isArray(formData.flow) && formData.flow.length > 0
|
|
50
|
+
? formData.flow[0]
|
|
51
|
+
: null;
|
|
52
|
+
|
|
53
|
+
// Find existing enter_flow action to preserve its UUID
|
|
54
|
+
const existingEnterFlowAction = originalNode.actions?.find(
|
|
55
|
+
(action) => action.type === 'enter_flow'
|
|
56
|
+
);
|
|
57
|
+
const enterFlowUuid = existingEnterFlowAction?.uuid || generateUUID();
|
|
58
|
+
|
|
59
|
+
// Create enter_flow action
|
|
60
|
+
const enterFlowAction: any = {
|
|
61
|
+
type: 'enter_flow',
|
|
62
|
+
uuid: enterFlowUuid,
|
|
63
|
+
flow: flowSelection
|
|
64
|
+
? {
|
|
65
|
+
uuid: flowSelection.uuid || flowSelection.value,
|
|
66
|
+
name: flowSelection.name
|
|
67
|
+
}
|
|
68
|
+
: { uuid: '', name: '' }
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Create categories and exits for Complete and Expired
|
|
72
|
+
const existingCategories = originalNode.router?.categories || [];
|
|
73
|
+
const existingExits = originalNode.exits || [];
|
|
74
|
+
const existingCases = originalNode.router?.cases || [];
|
|
75
|
+
|
|
76
|
+
// Find existing Complete category
|
|
77
|
+
const existingCompleteCategory = existingCategories.find(
|
|
78
|
+
(cat) => cat.name === 'Complete'
|
|
79
|
+
);
|
|
80
|
+
const existingCompleteExit = existingCompleteCategory
|
|
81
|
+
? existingExits.find(
|
|
82
|
+
(exit) => exit.uuid === existingCompleteCategory.exit_uuid
|
|
83
|
+
)
|
|
84
|
+
: null;
|
|
85
|
+
const existingCompleteCase = existingCompleteCategory
|
|
86
|
+
? existingCases.find(
|
|
87
|
+
(case_) => case_.category_uuid === existingCompleteCategory.uuid
|
|
88
|
+
)
|
|
89
|
+
: null;
|
|
90
|
+
|
|
91
|
+
const completeCategoryUuid =
|
|
92
|
+
existingCompleteCategory?.uuid || generateUUID();
|
|
93
|
+
const completeExitUuid = existingCompleteExit?.uuid || generateUUID();
|
|
94
|
+
const completeCaseUuid = existingCompleteCase?.uuid || generateUUID();
|
|
95
|
+
|
|
96
|
+
// Find existing Expired category
|
|
97
|
+
const existingExpiredCategory = existingCategories.find(
|
|
98
|
+
(cat) => cat.name === 'Expired'
|
|
99
|
+
);
|
|
100
|
+
const existingExpiredExit = existingExpiredCategory
|
|
101
|
+
? existingExits.find(
|
|
102
|
+
(exit) => exit.uuid === existingExpiredCategory.exit_uuid
|
|
103
|
+
)
|
|
104
|
+
: null;
|
|
105
|
+
|
|
106
|
+
const expiredCategoryUuid = existingExpiredCategory?.uuid || generateUUID();
|
|
107
|
+
const expiredExitUuid = existingExpiredExit?.uuid || generateUUID();
|
|
108
|
+
|
|
109
|
+
const categories = [
|
|
110
|
+
{
|
|
111
|
+
uuid: completeCategoryUuid,
|
|
112
|
+
name: 'Complete',
|
|
113
|
+
exit_uuid: completeExitUuid
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
uuid: expiredCategoryUuid,
|
|
117
|
+
name: 'Expired',
|
|
118
|
+
exit_uuid: expiredExitUuid
|
|
119
|
+
}
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
const exits = [
|
|
123
|
+
{
|
|
124
|
+
uuid: completeExitUuid,
|
|
125
|
+
destination_uuid: existingCompleteExit?.destination_uuid || null
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
uuid: expiredExitUuid,
|
|
129
|
+
destination_uuid: existingExpiredExit?.destination_uuid || null
|
|
130
|
+
}
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
const cases = [
|
|
134
|
+
{
|
|
135
|
+
uuid: completeCaseUuid,
|
|
136
|
+
type: 'has_only_text',
|
|
137
|
+
arguments: ['completed'],
|
|
138
|
+
category_uuid: completeCategoryUuid
|
|
139
|
+
}
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
// Create the router
|
|
143
|
+
const router = {
|
|
144
|
+
type: 'switch' as const,
|
|
145
|
+
categories: categories,
|
|
146
|
+
default_category_uuid: expiredCategoryUuid,
|
|
147
|
+
operand: '@child.status',
|
|
148
|
+
cases: cases
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// Return the complete node
|
|
152
|
+
return {
|
|
153
|
+
uuid: originalNode.uuid,
|
|
154
|
+
actions: [enterFlowAction],
|
|
155
|
+
router: router,
|
|
156
|
+
exits: exits
|
|
157
|
+
};
|
|
158
|
+
}
|
|
9
159
|
};
|
|
@@ -1,19 +1,142 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { COLORS, NodeConfig } from '../types';
|
|
2
|
+
import { Node, OpenTicket } from '../../store/flow-definition';
|
|
3
|
+
import { generateUUID, createSuccessFailureRouter } from '../../utils';
|
|
4
|
+
import { html } from 'lit';
|
|
3
5
|
|
|
4
6
|
export const split_by_ticket: NodeConfig = {
|
|
5
7
|
type: 'split_by_ticket',
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
name: 'Open Ticket',
|
|
9
|
+
color: COLORS.create,
|
|
10
|
+
form: {
|
|
11
|
+
topic: {
|
|
12
|
+
type: 'select',
|
|
13
|
+
label: 'Topic',
|
|
14
|
+
required: true,
|
|
15
|
+
placeholder: 'Select a topic',
|
|
16
|
+
options: [],
|
|
17
|
+
endpoint: '/api/v2/topics.json',
|
|
18
|
+
valueKey: 'uuid',
|
|
19
|
+
nameKey: 'name',
|
|
20
|
+
maxWidth: '200px'
|
|
21
|
+
},
|
|
22
|
+
assignee: {
|
|
23
|
+
type: 'select',
|
|
24
|
+
label: 'Assignee',
|
|
25
|
+
required: false,
|
|
26
|
+
placeholder: 'Select an agent (optional)',
|
|
27
|
+
options: [],
|
|
28
|
+
endpoint: '/api/v2/users.json',
|
|
29
|
+
valueKey: 'uuid',
|
|
30
|
+
getName: (item: {
|
|
31
|
+
first_name?: string;
|
|
32
|
+
last_name?: string;
|
|
33
|
+
name?: string;
|
|
34
|
+
}) => {
|
|
35
|
+
return item.name || [item.first_name, item.last_name].join(' ');
|
|
36
|
+
},
|
|
37
|
+
clearable: true
|
|
38
|
+
},
|
|
39
|
+
note: {
|
|
40
|
+
type: 'textarea',
|
|
41
|
+
label: 'Note',
|
|
42
|
+
required: false,
|
|
43
|
+
placeholder: 'Enter a note for the ticket (optional)',
|
|
44
|
+
minHeight: 100
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
layout: [{ type: 'row', items: ['topic', 'assignee'] }, 'note'],
|
|
48
|
+
render: (node: Node) => {
|
|
49
|
+
const openTicketAction = node.actions?.find(
|
|
50
|
+
(action) => action.type === 'open_ticket'
|
|
51
|
+
) as OpenTicket;
|
|
52
|
+
return html`
|
|
53
|
+
<div class="body">
|
|
54
|
+
${openTicketAction?.topic?.name || 'Configure ticket'}
|
|
55
|
+
</div>
|
|
56
|
+
`;
|
|
57
|
+
},
|
|
58
|
+
toFormData: (node: Node) => {
|
|
59
|
+
// Extract data from the existing node structure
|
|
60
|
+
const openTicketAction = node.actions?.find(
|
|
61
|
+
(action) => action.type === 'open_ticket'
|
|
62
|
+
) as any;
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
uuid: node.uuid,
|
|
66
|
+
topic: openTicketAction?.topic
|
|
67
|
+
? [
|
|
68
|
+
{
|
|
69
|
+
uuid: openTicketAction.topic.uuid,
|
|
70
|
+
name: openTicketAction.topic.name
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
: [],
|
|
74
|
+
assignee: openTicketAction?.assignee
|
|
75
|
+
? [
|
|
76
|
+
{
|
|
77
|
+
uuid: openTicketAction.assignee.uuid,
|
|
78
|
+
name: openTicketAction.assignee.name
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
: [],
|
|
82
|
+
note: openTicketAction?.note || ''
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
fromFormData: (formData: any, originalNode: Node): Node => {
|
|
86
|
+
// Find existing open_ticket action to preserve its UUID
|
|
87
|
+
const existingOpenTicketAction = originalNode.actions?.find(
|
|
88
|
+
(action) => action.type === 'open_ticket'
|
|
89
|
+
);
|
|
90
|
+
const openTicketUuid = existingOpenTicketAction?.uuid || generateUUID();
|
|
91
|
+
|
|
92
|
+
// Create open_ticket action
|
|
93
|
+
const openTicketAction: OpenTicket = {
|
|
94
|
+
type: 'open_ticket',
|
|
95
|
+
uuid: openTicketUuid,
|
|
96
|
+
topic:
|
|
97
|
+
formData.topic && formData.topic.length > 0
|
|
98
|
+
? {
|
|
99
|
+
uuid: formData.topic[0].uuid,
|
|
100
|
+
name: formData.topic[0].name
|
|
101
|
+
}
|
|
102
|
+
: undefined,
|
|
103
|
+
assignee:
|
|
104
|
+
formData.assignee && formData.assignee.length > 0
|
|
105
|
+
? {
|
|
106
|
+
uuid: formData.assignee[0].uuid,
|
|
107
|
+
name:
|
|
108
|
+
formData.assignee[0].name ||
|
|
109
|
+
[
|
|
110
|
+
formData.assignee[0].first_name,
|
|
111
|
+
formData.assignee[0].last_name
|
|
112
|
+
].join(' ')
|
|
113
|
+
}
|
|
114
|
+
: undefined,
|
|
115
|
+
note: formData.note || ''
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// Create categories and exits for Success and Failure
|
|
119
|
+
const existingCategories = originalNode.router?.categories || [];
|
|
120
|
+
const existingExits = originalNode.exits || [];
|
|
121
|
+
const existingCases = originalNode.router?.cases || [];
|
|
122
|
+
|
|
123
|
+
const { router, exits } = createSuccessFailureRouter(
|
|
124
|
+
'@locals._new_ticket',
|
|
12
125
|
{
|
|
13
126
|
type: 'has_text',
|
|
14
|
-
arguments: []
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
127
|
+
arguments: []
|
|
128
|
+
},
|
|
129
|
+
existingCategories,
|
|
130
|
+
existingExits,
|
|
131
|
+
existingCases
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// Return the complete node
|
|
135
|
+
return {
|
|
136
|
+
uuid: originalNode.uuid,
|
|
137
|
+
actions: [openTicketAction],
|
|
138
|
+
router: router,
|
|
139
|
+
exits: exits
|
|
140
|
+
};
|
|
18
141
|
}
|
|
19
142
|
};
|
|
@@ -1,19 +1,194 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { COLORS, NodeConfig } from '../types';
|
|
2
|
+
import { CallWebhook, Node } from '../../store/flow-definition';
|
|
3
|
+
import { generateUUID, createSuccessFailureRouter } from '../../utils';
|
|
4
|
+
import { html } from 'lit';
|
|
5
|
+
|
|
6
|
+
const defaultPost = `@(json(object(
|
|
7
|
+
"contact", object(
|
|
8
|
+
"uuid", contact.uuid,
|
|
9
|
+
"name", contact.name,
|
|
10
|
+
"urn", contact.urn
|
|
11
|
+
),
|
|
12
|
+
"flow", object(
|
|
13
|
+
"uuid", run.flow.uuid,
|
|
14
|
+
"name", run.flow.name
|
|
15
|
+
),
|
|
16
|
+
"results", foreach_value(results, extract_object, "value", "category")
|
|
17
|
+
)))`;
|
|
3
18
|
|
|
4
19
|
export const split_by_webhook: NodeConfig = {
|
|
5
20
|
type: 'split_by_webhook',
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
21
|
+
name: 'Call Webhook',
|
|
22
|
+
color: COLORS.call,
|
|
23
|
+
form: {
|
|
24
|
+
method: {
|
|
25
|
+
type: 'select',
|
|
26
|
+
required: true,
|
|
27
|
+
options: ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'PATCH'],
|
|
28
|
+
maxWidth: '120px',
|
|
29
|
+
searchable: false
|
|
30
|
+
},
|
|
31
|
+
url: {
|
|
32
|
+
type: 'text',
|
|
33
|
+
required: true,
|
|
34
|
+
evaluated: true,
|
|
35
|
+
placeholder: 'https://example.com/webhook'
|
|
36
|
+
},
|
|
37
|
+
headers: {
|
|
38
|
+
type: 'key-value',
|
|
39
|
+
sortable: true,
|
|
40
|
+
keyPlaceholder: 'Header name',
|
|
41
|
+
valuePlaceholder: 'Header value',
|
|
42
|
+
minRows: 0
|
|
43
|
+
},
|
|
44
|
+
body: {
|
|
45
|
+
type: 'textarea',
|
|
46
|
+
evaluated: true,
|
|
47
|
+
placeholder: 'Request body content (JSON, XML, etc.)',
|
|
48
|
+
minHeight: 200,
|
|
49
|
+
dependsOn: ['method'],
|
|
50
|
+
computeValue: (
|
|
51
|
+
values: Record<string, any>,
|
|
52
|
+
currentValue: any,
|
|
53
|
+
originalValues?: Record<string, any>
|
|
54
|
+
) => {
|
|
55
|
+
// Check if method is POST (handle both string and select object formats)
|
|
56
|
+
const method =
|
|
57
|
+
Array.isArray(values.method) && values.method.length > 0
|
|
58
|
+
? values.method[0].value || values.method[0].name
|
|
59
|
+
: values.method;
|
|
60
|
+
|
|
61
|
+
if (method === 'POST') {
|
|
62
|
+
// For POST, provide the template if body is empty or was never set by user
|
|
63
|
+
if (!currentValue || currentValue.trim() === '') {
|
|
64
|
+
return defaultPost;
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
// For non-POST methods, clear the body if it was auto-generated or empty
|
|
68
|
+
// Check if the original body was empty (user never specified a body)
|
|
69
|
+
const originalBody = originalValues?.body || '';
|
|
70
|
+
const isOriginallyEmpty = !originalBody || originalBody.trim() === '';
|
|
71
|
+
|
|
72
|
+
// Clear if: originally empty, contains default template, or is currently empty
|
|
73
|
+
if (
|
|
74
|
+
isOriginallyEmpty ||
|
|
75
|
+
!currentValue ||
|
|
76
|
+
currentValue.trim() === '' ||
|
|
77
|
+
currentValue.trim() === defaultPost.trim()
|
|
78
|
+
) {
|
|
79
|
+
return '';
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return currentValue; // Keep existing value if user has customized it
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
layout: [
|
|
88
|
+
// Row with method and URL side by side
|
|
89
|
+
{ type: 'row', items: ['method', 'url'] },
|
|
90
|
+
// Advanced group with nested layouts
|
|
91
|
+
{
|
|
92
|
+
type: 'group',
|
|
93
|
+
label: 'Headers',
|
|
94
|
+
items: ['headers'],
|
|
95
|
+
collapsible: true,
|
|
96
|
+
collapsed: true,
|
|
97
|
+
helpText: 'Configure authentication or custom headers',
|
|
98
|
+
getGroupValueCount: (formData: any) => {
|
|
99
|
+
return formData.headers?.length || 0;
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
type: 'group',
|
|
104
|
+
label: 'Body',
|
|
105
|
+
items: ['body'],
|
|
106
|
+
collapsible: true,
|
|
107
|
+
collapsed: true,
|
|
108
|
+
helpText: 'Configure the request payload',
|
|
109
|
+
getGroupValueCount: (formData: any) => {
|
|
110
|
+
return !!(
|
|
111
|
+
formData.body &&
|
|
112
|
+
formData.body.trim() !== '' &&
|
|
113
|
+
formData.body !== defaultPost
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
],
|
|
118
|
+
render: (node: Node) => {
|
|
119
|
+
const callWebhookAction = node.actions?.find(
|
|
120
|
+
(action) => action.type === 'call_webhook'
|
|
121
|
+
) as CallWebhook;
|
|
122
|
+
return html`
|
|
123
|
+
<div
|
|
124
|
+
class="body"
|
|
125
|
+
style="word-wrap: break-word; overflow-wrap: break-word; hyphens: auto;"
|
|
126
|
+
>
|
|
127
|
+
${callWebhookAction?.url || 'Configure webhook'}
|
|
128
|
+
</div>
|
|
129
|
+
`;
|
|
130
|
+
},
|
|
131
|
+
toFormData: (node: Node) => {
|
|
132
|
+
// Extract data from the existing node structure
|
|
133
|
+
const callWebhookAction = node.actions?.find(
|
|
134
|
+
(action) => action.type === 'call_webhook'
|
|
135
|
+
) as any;
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
uuid: node.uuid,
|
|
139
|
+
method: callWebhookAction?.method
|
|
140
|
+
? [{ value: callWebhookAction.method, name: callWebhookAction.method }]
|
|
141
|
+
: [{ value: 'GET', name: 'GET' }],
|
|
142
|
+
url: callWebhookAction?.url || '',
|
|
143
|
+
headers: callWebhookAction?.headers || [],
|
|
144
|
+
body: callWebhookAction?.body || ''
|
|
145
|
+
};
|
|
146
|
+
},
|
|
147
|
+
fromFormData: (formData: any, originalNode: Node): Node => {
|
|
148
|
+
// Get method selection
|
|
149
|
+
const methodSelection =
|
|
150
|
+
Array.isArray(formData.method) && formData.method.length > 0
|
|
151
|
+
? formData.method[0]
|
|
152
|
+
: { value: 'GET', name: 'GET' };
|
|
153
|
+
|
|
154
|
+
// Find existing call_webhook action to preserve its UUID
|
|
155
|
+
const existingCallWebhookAction = originalNode.actions?.find(
|
|
156
|
+
(action) => action.type === 'call_webhook'
|
|
157
|
+
);
|
|
158
|
+
const callWebhookUuid = existingCallWebhookAction?.uuid || generateUUID();
|
|
159
|
+
|
|
160
|
+
// Create call_webhook action
|
|
161
|
+
const callWebhookAction: CallWebhook = {
|
|
162
|
+
type: 'call_webhook',
|
|
163
|
+
uuid: callWebhookUuid,
|
|
164
|
+
method: methodSelection.value,
|
|
165
|
+
url: formData.url || '',
|
|
166
|
+
headers: formData.headers || [],
|
|
167
|
+
body: formData.body || ''
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// Create categories and exits for Success and Failure
|
|
171
|
+
const existingCategories = originalNode.router?.categories || [];
|
|
172
|
+
const existingExits = originalNode.exits || [];
|
|
173
|
+
const existingCases = originalNode.router?.cases || [];
|
|
174
|
+
|
|
175
|
+
const { router, exits } = createSuccessFailureRouter(
|
|
176
|
+
'@webhook.status',
|
|
12
177
|
{
|
|
13
178
|
type: 'has_number_between',
|
|
14
|
-
arguments: ['200', '299']
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
179
|
+
arguments: ['200', '299']
|
|
180
|
+
},
|
|
181
|
+
existingCategories,
|
|
182
|
+
existingExits,
|
|
183
|
+
existingCases
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
// Return the complete node
|
|
187
|
+
return {
|
|
188
|
+
uuid: originalNode.uuid,
|
|
189
|
+
actions: [callWebhookAction],
|
|
190
|
+
router: router,
|
|
191
|
+
exits: exits
|
|
192
|
+
};
|
|
18
193
|
}
|
|
19
194
|
};
|