@nyaruka/temba-components 0.130.5 → 0.131.0
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 +3 -20
- package/dist/temba-components.js +65 -64
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_random.js +1 -0
- package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js +254 -65
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
- package/out-tsc/src/form/ArrayEditor.js +195 -2
- package/out-tsc/src/form/ArrayEditor.js.map +1 -1
- package/out-tsc/src/form/select/Omnibox.js +4 -0
- package/out-tsc/src/form/select/Omnibox.js.map +1 -1
- package/out-tsc/test/nodes/wait_for_response.test.js +373 -8
- package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -1
- package/package.json +1 -1
- 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_response/render/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
- package/src/flow/nodes/split_by_random.ts +1 -0
- package/src/flow/nodes/wait_for_response.ts +327 -72
- package/src/form/ArrayEditor.ts +260 -2
- package/src/form/select/Omnibox.ts +3 -0
- package/test/nodes/wait_for_response.test.ts +426 -8
|
@@ -28,6 +28,129 @@ const TIMEOUT_OPTIONS = [
|
|
|
28
28
|
{ value: '604800', name: '1 week' }
|
|
29
29
|
];
|
|
30
30
|
|
|
31
|
+
// Helper function to check if a category is a system category
|
|
32
|
+
const isSystemCategory = (categoryName: string): boolean => {
|
|
33
|
+
return ['No Response', 'Other', 'All Responses', 'Timeout'].includes(
|
|
34
|
+
categoryName
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Helper function to check if a UUID belongs to a system category
|
|
39
|
+
const isSystemCategoryUuid = (
|
|
40
|
+
uuid: string,
|
|
41
|
+
categories: Category[]
|
|
42
|
+
): boolean => {
|
|
43
|
+
const category = categories.find((cat) => cat.uuid === uuid);
|
|
44
|
+
return category ? isSystemCategory(category.name) : false;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Helper function to generate default category name based on operator and operands
|
|
48
|
+
const generateDefaultCategoryName = (
|
|
49
|
+
operator: string,
|
|
50
|
+
value1?: string,
|
|
51
|
+
value2?: string
|
|
52
|
+
): string => {
|
|
53
|
+
const operatorConfig = getOperatorConfig(operator);
|
|
54
|
+
if (!operatorConfig) return '';
|
|
55
|
+
|
|
56
|
+
// Fixed category names (no operands)
|
|
57
|
+
if (operatorConfig.operands === 0) {
|
|
58
|
+
return operatorConfig.categoryName || '';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Dynamic category names based on operands
|
|
62
|
+
const cleanValue1 = (value1 || '').trim();
|
|
63
|
+
const cleanValue2 = (value2 || '').trim();
|
|
64
|
+
|
|
65
|
+
// Helper to capitalize first letter
|
|
66
|
+
const capitalize = (str: string) => {
|
|
67
|
+
if (!str) return '';
|
|
68
|
+
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Handle different operator types
|
|
72
|
+
switch (operator) {
|
|
73
|
+
// Word/phrase operators - capitalize first letter of value
|
|
74
|
+
case 'has_any_word':
|
|
75
|
+
case 'has_all_words':
|
|
76
|
+
case 'has_phrase':
|
|
77
|
+
case 'has_only_phrase':
|
|
78
|
+
case 'has_beginning':
|
|
79
|
+
return cleanValue1 ? capitalize(cleanValue1) : '';
|
|
80
|
+
|
|
81
|
+
// Pattern operators - show as-is
|
|
82
|
+
case 'has_pattern':
|
|
83
|
+
return cleanValue1;
|
|
84
|
+
|
|
85
|
+
// Number comparison operators - include symbol
|
|
86
|
+
case 'has_number_eq':
|
|
87
|
+
return cleanValue1 ? `= ${cleanValue1}` : '';
|
|
88
|
+
case 'has_number_lt':
|
|
89
|
+
return cleanValue1 ? `< ${cleanValue1}` : '';
|
|
90
|
+
case 'has_number_lte':
|
|
91
|
+
return cleanValue1 ? `≤ ${cleanValue1}` : '';
|
|
92
|
+
case 'has_number_gt':
|
|
93
|
+
return cleanValue1 ? `> ${cleanValue1}` : '';
|
|
94
|
+
case 'has_number_gte':
|
|
95
|
+
return cleanValue1 ? `≥ ${cleanValue1}` : '';
|
|
96
|
+
|
|
97
|
+
// Number between - range format
|
|
98
|
+
case 'has_number_between':
|
|
99
|
+
if (cleanValue1 && cleanValue2) {
|
|
100
|
+
return `${cleanValue1} - ${cleanValue2}`;
|
|
101
|
+
}
|
|
102
|
+
return '';
|
|
103
|
+
|
|
104
|
+
// Date operators - format with relative expressions
|
|
105
|
+
case 'has_date_lt':
|
|
106
|
+
case 'has_date_lte':
|
|
107
|
+
if (cleanValue1) {
|
|
108
|
+
// Parse relative date expression (e.g., "today + 5" or "today - 3")
|
|
109
|
+
const match = cleanValue1.match(/^(today)\s*([+-])\s*(\d+)$/i);
|
|
110
|
+
if (match) {
|
|
111
|
+
const [, base, operator, days] = match;
|
|
112
|
+
const dayWord = days === '1' ? 'day' : 'days';
|
|
113
|
+
return `Before ${base} ${operator} ${days} ${dayWord}`;
|
|
114
|
+
}
|
|
115
|
+
// Fallback for other date formats
|
|
116
|
+
return `Before ${cleanValue1}`;
|
|
117
|
+
}
|
|
118
|
+
return '';
|
|
119
|
+
|
|
120
|
+
case 'has_date_gt':
|
|
121
|
+
case 'has_date_gte':
|
|
122
|
+
if (cleanValue1) {
|
|
123
|
+
// Parse relative date expression
|
|
124
|
+
const match = cleanValue1.match(/^(today)\s*([+-])\s*(\d+)$/i);
|
|
125
|
+
if (match) {
|
|
126
|
+
const [, base, operator, days] = match;
|
|
127
|
+
const dayWord = days === '1' ? 'day' : 'days';
|
|
128
|
+
return `After ${base} ${operator} ${days} ${dayWord}`;
|
|
129
|
+
}
|
|
130
|
+
// Fallback for other date formats
|
|
131
|
+
return `After ${cleanValue1}`;
|
|
132
|
+
}
|
|
133
|
+
return '';
|
|
134
|
+
|
|
135
|
+
case 'has_date_eq':
|
|
136
|
+
if (cleanValue1) {
|
|
137
|
+
// Parse relative date expression
|
|
138
|
+
const match = cleanValue1.match(/^(today)\s*([+-])\s*(\d+)$/i);
|
|
139
|
+
if (match) {
|
|
140
|
+
const [, base, operator, days] = match;
|
|
141
|
+
const dayWord = days === '1' ? 'day' : 'days';
|
|
142
|
+
return `${base} ${operator} ${days} ${dayWord}`;
|
|
143
|
+
}
|
|
144
|
+
return cleanValue1;
|
|
145
|
+
}
|
|
146
|
+
return '';
|
|
147
|
+
|
|
148
|
+
default:
|
|
149
|
+
// Fallback - capitalize first value
|
|
150
|
+
return cleanValue1 ? capitalize(cleanValue1) : '';
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
31
154
|
// Helper function to create a wait_for_response router with user rules
|
|
32
155
|
const createWaitForResponseRouter = (
|
|
33
156
|
userRules: any[],
|
|
@@ -41,10 +164,7 @@ const createWaitForResponseRouter = (
|
|
|
41
164
|
|
|
42
165
|
// Filter existing categories to get only user-defined rules (exclude system categories)
|
|
43
166
|
const existingUserCategories = existingCategories.filter(
|
|
44
|
-
(cat) =>
|
|
45
|
-
cat.name !== 'No Response' &&
|
|
46
|
-
cat.name !== 'Other' &&
|
|
47
|
-
cat.name !== 'Timeout'
|
|
167
|
+
(cat) => !isSystemCategory(cat.name)
|
|
48
168
|
);
|
|
49
169
|
|
|
50
170
|
// Track categories as we create them (case-insensitive lookup)
|
|
@@ -74,15 +194,29 @@ const createWaitForResponseRouter = (
|
|
|
74
194
|
!existingCategory &&
|
|
75
195
|
categoryCreationOrder < existingUserCategories.length
|
|
76
196
|
) {
|
|
77
|
-
|
|
197
|
+
const candidateCategory = existingUserCategories[categoryCreationOrder];
|
|
198
|
+
// Double-check that this candidate is not a system category UUID
|
|
199
|
+
if (
|
|
200
|
+
candidateCategory &&
|
|
201
|
+
!isSystemCategoryUuid(candidateCategory.uuid, existingCategories)
|
|
202
|
+
) {
|
|
203
|
+
existingCategory = candidateCategory;
|
|
204
|
+
}
|
|
78
205
|
}
|
|
79
206
|
|
|
80
207
|
const existingExit = existingCategory
|
|
81
208
|
? existingExits.find((exit) => exit.uuid === existingCategory.exit_uuid)
|
|
82
209
|
: null;
|
|
83
210
|
|
|
84
|
-
|
|
85
|
-
|
|
211
|
+
// Generate UUIDs, ensuring we don't reuse system category UUIDs
|
|
212
|
+
let exitUuid = existingExit?.uuid || generateUUID();
|
|
213
|
+
let categoryUuid = existingCategory?.uuid || generateUUID();
|
|
214
|
+
|
|
215
|
+
// Additional safety check: if somehow we got a system category UUID, generate new ones
|
|
216
|
+
if (isSystemCategoryUuid(categoryUuid, existingCategories)) {
|
|
217
|
+
categoryUuid = generateUUID();
|
|
218
|
+
exitUuid = generateUUID();
|
|
219
|
+
}
|
|
86
220
|
|
|
87
221
|
categoryInfo = {
|
|
88
222
|
uuid: categoryUuid,
|
|
@@ -169,52 +303,69 @@ const createWaitForResponseRouter = (
|
|
|
169
303
|
});
|
|
170
304
|
});
|
|
171
305
|
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const existingExit = existingExits.find(
|
|
176
|
-
(exit) => exit.uuid === category.exit_uuid
|
|
177
|
-
);
|
|
306
|
+
// Add default category (always present)
|
|
307
|
+
// Name is "Other" if there are user rules, "All Responses" if there are no user rules
|
|
308
|
+
const defaultCategoryName = userRules.length > 0 ? 'Other' : 'All Responses';
|
|
178
309
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
310
|
+
// Try to find existing default category by name (prefer exact match)
|
|
311
|
+
let existingDefaultCategory = existingCategories.find(
|
|
312
|
+
(cat) => cat.name === defaultCategoryName
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
// If no exact match, try to find the other possible default category name
|
|
316
|
+
if (!existingDefaultCategory) {
|
|
317
|
+
const alternateName = userRules.length > 0 ? 'All Responses' : 'Other';
|
|
318
|
+
existingDefaultCategory = existingCategories.find(
|
|
319
|
+
(cat) => cat.name === alternateName
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const existingDefaultExit = existingDefaultCategory
|
|
324
|
+
? existingExits.find(
|
|
325
|
+
(exit) => exit.uuid === existingDefaultCategory.exit_uuid
|
|
326
|
+
)
|
|
327
|
+
: null;
|
|
328
|
+
|
|
329
|
+
const defaultExitUuid = existingDefaultExit?.uuid || generateUUID();
|
|
330
|
+
const defaultCategoryUuid = existingDefaultCategory?.uuid || generateUUID();
|
|
331
|
+
|
|
332
|
+
categories.push({
|
|
333
|
+
uuid: defaultCategoryUuid,
|
|
334
|
+
name: defaultCategoryName,
|
|
335
|
+
exit_uuid: defaultExitUuid
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
exits.push({
|
|
339
|
+
uuid: defaultExitUuid,
|
|
340
|
+
destination_uuid: existingDefaultExit?.destination_uuid || null
|
|
184
341
|
});
|
|
185
342
|
|
|
186
|
-
// Add "
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
343
|
+
// Add "No Response" category last (if it exists in the original)
|
|
344
|
+
const existingNoResponseCategory = existingCategories.find(
|
|
345
|
+
(cat) => cat.name === 'No Response' || cat.name === 'Timeout'
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
if (existingNoResponseCategory) {
|
|
349
|
+
const existingNoResponseExit = existingExits.find(
|
|
350
|
+
(exit) => exit.uuid === existingNoResponseCategory.exit_uuid
|
|
190
351
|
);
|
|
191
|
-
const existingOtherExit = existingOtherCategory
|
|
192
|
-
? existingExits.find(
|
|
193
|
-
(exit) => exit.uuid === existingOtherCategory.exit_uuid
|
|
194
|
-
)
|
|
195
|
-
: null;
|
|
196
|
-
|
|
197
|
-
const otherExitUuid = existingOtherExit?.uuid || generateUUID();
|
|
198
|
-
const otherCategoryUuid = existingOtherCategory?.uuid || generateUUID();
|
|
199
|
-
|
|
200
|
-
categories.push({
|
|
201
|
-
uuid: otherCategoryUuid,
|
|
202
|
-
name: 'Other',
|
|
203
|
-
exit_uuid: otherExitUuid
|
|
204
|
-
});
|
|
205
352
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
353
|
+
if (existingNoResponseExit) {
|
|
354
|
+
categories.push(existingNoResponseCategory);
|
|
355
|
+
exits.push(existingNoResponseExit);
|
|
356
|
+
}
|
|
210
357
|
}
|
|
211
358
|
|
|
359
|
+
// Find the default category (either "Other" or "All Responses")
|
|
360
|
+
const defaultCategory = categories.find(
|
|
361
|
+
(cat) => cat.name === 'Other' || cat.name === 'All Responses'
|
|
362
|
+
);
|
|
363
|
+
|
|
212
364
|
return {
|
|
213
365
|
router: {
|
|
214
366
|
type: 'switch' as const,
|
|
215
367
|
categories: categories,
|
|
216
|
-
default_category_uuid:
|
|
217
|
-
?.uuid,
|
|
368
|
+
default_category_uuid: defaultCategory?.uuid,
|
|
218
369
|
operand: '@input.text',
|
|
219
370
|
cases: cases
|
|
220
371
|
},
|
|
@@ -286,6 +437,79 @@ export const wait_for_response: NodeConfig = {
|
|
|
286
437
|
// No value required for this operator
|
|
287
438
|
return false;
|
|
288
439
|
},
|
|
440
|
+
onItemChange: (
|
|
441
|
+
itemIndex: number,
|
|
442
|
+
field: string,
|
|
443
|
+
value: any,
|
|
444
|
+
allItems: any[]
|
|
445
|
+
) => {
|
|
446
|
+
const updatedItems = [...allItems];
|
|
447
|
+
const item = { ...updatedItems[itemIndex] };
|
|
448
|
+
|
|
449
|
+
// Helper to get operator value from various formats
|
|
450
|
+
const getOperatorValue = (operator: any): string => {
|
|
451
|
+
if (typeof operator === 'string') {
|
|
452
|
+
return operator.trim();
|
|
453
|
+
} else if (Array.isArray(operator) && operator.length > 0) {
|
|
454
|
+
const firstOperator = operator[0];
|
|
455
|
+
if (
|
|
456
|
+
firstOperator &&
|
|
457
|
+
typeof firstOperator === 'object' &&
|
|
458
|
+
firstOperator.value
|
|
459
|
+
) {
|
|
460
|
+
return firstOperator.value.trim();
|
|
461
|
+
}
|
|
462
|
+
} else if (
|
|
463
|
+
operator &&
|
|
464
|
+
typeof operator === 'object' &&
|
|
465
|
+
operator.value
|
|
466
|
+
) {
|
|
467
|
+
return operator.value.trim();
|
|
468
|
+
}
|
|
469
|
+
return '';
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
// Update the changed field
|
|
473
|
+
item[field] = value;
|
|
474
|
+
|
|
475
|
+
// Get operator values (before and after the change)
|
|
476
|
+
const oldItem = allItems[itemIndex] || {};
|
|
477
|
+
const oldOperatorValue =
|
|
478
|
+
field === 'operator'
|
|
479
|
+
? getOperatorValue(oldItem.operator)
|
|
480
|
+
: getOperatorValue(item.operator);
|
|
481
|
+
const newOperatorValue = getOperatorValue(item.operator);
|
|
482
|
+
|
|
483
|
+
// Calculate what the default category name should be before the change
|
|
484
|
+
const oldDefaultCategory = generateDefaultCategoryName(
|
|
485
|
+
oldOperatorValue,
|
|
486
|
+
field === 'value1' ? oldItem.value1 : item.value1,
|
|
487
|
+
field === 'value2' ? oldItem.value2 : item.value2
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
// Calculate what the new default category name should be after the change
|
|
491
|
+
const newDefaultCategory = generateDefaultCategoryName(
|
|
492
|
+
newOperatorValue,
|
|
493
|
+
item.value1,
|
|
494
|
+
item.value2
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
// Determine if we should auto-update the category
|
|
498
|
+
const shouldUpdateCategory =
|
|
499
|
+
// Category is empty
|
|
500
|
+
!item.category ||
|
|
501
|
+
item.category.trim() === '' ||
|
|
502
|
+
// Category matches the old default (user hasn't customized it)
|
|
503
|
+
item.category === oldDefaultCategory;
|
|
504
|
+
|
|
505
|
+
// Auto-populate or update category if conditions are met
|
|
506
|
+
if (shouldUpdateCategory && newDefaultCategory) {
|
|
507
|
+
item.category = newDefaultCategory;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
updatedItems[itemIndex] = item;
|
|
511
|
+
return updatedItems;
|
|
512
|
+
},
|
|
289
513
|
itemConfig: {
|
|
290
514
|
operator: {
|
|
291
515
|
type: 'select',
|
|
@@ -432,12 +656,8 @@ export const wait_for_response: NodeConfig = {
|
|
|
432
656
|
(cat) => cat.uuid === case_.category_uuid
|
|
433
657
|
);
|
|
434
658
|
|
|
435
|
-
// Skip
|
|
436
|
-
if (
|
|
437
|
-
category &&
|
|
438
|
-
category.name !== 'No Response' &&
|
|
439
|
-
category.name !== 'Other'
|
|
440
|
-
) {
|
|
659
|
+
// Skip system categories
|
|
660
|
+
if (category && !isSystemCategory(category.name)) {
|
|
441
661
|
// Handle different operator types
|
|
442
662
|
const operatorConfig = getOperatorConfig(case_.type);
|
|
443
663
|
const operatorDisplayName = operatorConfig
|
|
@@ -574,16 +794,60 @@ export const wait_for_response: NodeConfig = {
|
|
|
574
794
|
|
|
575
795
|
// If no user rules, clear cases but preserve other router config
|
|
576
796
|
if (userRules.length === 0) {
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
};
|
|
797
|
+
// Get existing router data for preservation
|
|
798
|
+
let existingCategories = originalNode.router?.categories || [];
|
|
799
|
+
const existingExits = [...(originalNode.exits || [])]; // Create a copy to avoid extensibility issues
|
|
581
800
|
|
|
582
|
-
//
|
|
583
|
-
if
|
|
584
|
-
|
|
801
|
+
// Handle timeout: ensure "No Response" category exists if timeout is enabled,
|
|
802
|
+
// or remove it if timeout is disabled
|
|
803
|
+
if (formData.timeout_enabled) {
|
|
804
|
+
let noResponseCategory = existingCategories.find(
|
|
805
|
+
(cat: any) => cat.name === 'No Response'
|
|
806
|
+
);
|
|
807
|
+
|
|
808
|
+
if (!noResponseCategory) {
|
|
809
|
+
// Create new "No Response" category and exit
|
|
810
|
+
const noResponseExitUuid = generateUUID();
|
|
811
|
+
noResponseCategory = {
|
|
812
|
+
uuid: generateUUID(),
|
|
813
|
+
name: 'No Response',
|
|
814
|
+
exit_uuid: noResponseExitUuid
|
|
815
|
+
};
|
|
816
|
+
|
|
817
|
+
// Add to existing categories for processing
|
|
818
|
+
existingCategories = [...existingCategories, noResponseCategory];
|
|
819
|
+
|
|
820
|
+
// Add corresponding exit if it doesn't exist
|
|
821
|
+
if (!existingExits.find((exit) => exit.uuid === noResponseExitUuid)) {
|
|
822
|
+
existingExits.push({
|
|
823
|
+
uuid: noResponseExitUuid,
|
|
824
|
+
destination_uuid: null
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
} else {
|
|
829
|
+
// If timeout is disabled, remove "No Response" category from existing categories
|
|
830
|
+
existingCategories = existingCategories.filter(
|
|
831
|
+
(cat: any) => cat.name !== 'No Response'
|
|
832
|
+
);
|
|
585
833
|
}
|
|
586
834
|
|
|
835
|
+
// Create router with "All Responses" as default category
|
|
836
|
+
// This will now properly handle the "No Response" category if it exists
|
|
837
|
+
const { router: noRulesRouter, exits: noRulesExits } =
|
|
838
|
+
createWaitForResponseRouter(
|
|
839
|
+
[], // No user rules
|
|
840
|
+
existingCategories,
|
|
841
|
+
existingExits,
|
|
842
|
+
[] // No cases
|
|
843
|
+
);
|
|
844
|
+
|
|
845
|
+
const router: any = {
|
|
846
|
+
...noRulesRouter,
|
|
847
|
+
result_name: formData.result_name || 'response',
|
|
848
|
+
cases: [] // Clear all cases when no rules
|
|
849
|
+
};
|
|
850
|
+
|
|
587
851
|
// Build wait configuration based on form data
|
|
588
852
|
const waitConfig: any = {
|
|
589
853
|
type: 'msg'
|
|
@@ -622,34 +886,25 @@ export const wait_for_response: NodeConfig = {
|
|
|
622
886
|
timeoutSeconds = 300; // Default to 5 minutes
|
|
623
887
|
}
|
|
624
888
|
|
|
625
|
-
// Find
|
|
626
|
-
|
|
889
|
+
// Find the "No Response" category (should exist now)
|
|
890
|
+
const noResponseCategory = router.categories.find(
|
|
627
891
|
(cat: any) => cat.name === 'No Response'
|
|
628
892
|
);
|
|
629
893
|
|
|
630
|
-
if (
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
exit_uuid: generateUUID()
|
|
894
|
+
if (noResponseCategory) {
|
|
895
|
+
waitConfig.timeout = {
|
|
896
|
+
seconds: timeoutSeconds,
|
|
897
|
+
category_uuid: noResponseCategory.uuid
|
|
635
898
|
};
|
|
636
|
-
|
|
637
|
-
// Add to router categories
|
|
638
|
-
router.categories = router.categories || [];
|
|
639
|
-
router.categories.push(noResponseCategory);
|
|
640
899
|
}
|
|
641
|
-
|
|
642
|
-
waitConfig.timeout = {
|
|
643
|
-
seconds: timeoutSeconds,
|
|
644
|
-
category_uuid: noResponseCategory.uuid
|
|
645
|
-
};
|
|
646
900
|
}
|
|
647
901
|
|
|
648
902
|
router.wait = waitConfig;
|
|
649
903
|
|
|
650
904
|
return {
|
|
651
905
|
...originalNode,
|
|
652
|
-
router
|
|
906
|
+
router,
|
|
907
|
+
exits: noRulesExits
|
|
653
908
|
};
|
|
654
909
|
}
|
|
655
910
|
|