@nyaruka/temba-components 0.130.5 → 0.131.1

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.
Files changed (30) hide show
  1. package/CHANGELOG.md +9 -20
  2. package/dist/temba-components.js +66 -65
  3. package/dist/temba-components.js.map +1 -1
  4. package/out-tsc/src/flow/nodes/split_by_random.js +1 -0
  5. package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
  6. package/out-tsc/src/flow/nodes/wait_for_response.js +254 -65
  7. package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
  8. package/out-tsc/src/form/ArrayEditor.js +195 -2
  9. package/out-tsc/src/form/ArrayEditor.js.map +1 -1
  10. package/out-tsc/src/form/ContactSearch.js +1 -1
  11. package/out-tsc/src/form/ContactSearch.js.map +1 -1
  12. package/out-tsc/src/form/select/Omnibox.js +4 -0
  13. package/out-tsc/src/form/select/Omnibox.js.map +1 -1
  14. package/out-tsc/test/nodes/wait_for_response.test.js +373 -8
  15. package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -1
  16. package/package.json +1 -1
  17. package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
  18. package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
  19. package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
  20. package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
  21. package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
  22. package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
  23. package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
  24. package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
  25. package/src/flow/nodes/split_by_random.ts +1 -0
  26. package/src/flow/nodes/wait_for_response.ts +327 -72
  27. package/src/form/ArrayEditor.ts +260 -2
  28. package/src/form/ContactSearch.ts +1 -1
  29. package/src/form/select/Omnibox.ts +3 -0
  30. package/test/nodes/wait_for_response.test.ts +426 -8
@@ -42,6 +42,7 @@ export const split_by_random = {
42
42
  helpText: 'Define the buckets to randomly split contacts into',
43
43
  required: true,
44
44
  itemLabel: 'Bucket',
45
+ sortable: true,
45
46
  minItems: 2,
46
47
  maxItems: 10,
47
48
  isEmptyItem: (item) => {
@@ -1 +1 @@
1
- {"version":3,"file":"split_by_random.js","sourceRoot":"","sources":["../../../../src/flow/nodes/split_by_random.ts"],"names":[],"mappings":"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","sourcesContent":["import { COLORS, NodeConfig } from '../types';\nimport { Node, Category, Exit } from '../../store/flow-definition.d';\nimport { generateUUID } from '../../utils';\n\n// Helper function to create a random router with categories\nconst createRandomRouter = (\n userCategories: string[],\n existingCategories: Category[] = [],\n existingExits: Exit[] = []\n) => {\n const categories: Category[] = [];\n const exits: Exit[] = [];\n\n // Create categories and exits for user-defined buckets\n userCategories.forEach((categoryName) => {\n // Try to find existing category by name\n const existingCategory = existingCategories.find(\n (cat) => cat.name === categoryName\n );\n const existingExit = existingCategory\n ? existingExits.find((exit) => exit.uuid === existingCategory.exit_uuid)\n : null;\n\n const exitUuid = existingExit?.uuid || generateUUID();\n const categoryUuid = existingCategory?.uuid || generateUUID();\n\n categories.push({\n uuid: categoryUuid,\n name: categoryName,\n exit_uuid: exitUuid\n });\n\n exits.push({\n uuid: exitUuid,\n destination_uuid: existingExit?.destination_uuid || null\n });\n });\n\n return {\n router: {\n type: 'random' as const,\n categories: categories\n },\n exits: exits\n };\n};\n\nexport const split_by_random: NodeConfig = {\n type: 'split_by_random',\n name: 'Split by Random',\n color: COLORS.split,\n form: {\n categories: {\n type: 'array',\n label: 'Buckets',\n helpText: 'Define the buckets to randomly split contacts into',\n required: true,\n itemLabel: 'Bucket',\n minItems: 2,\n maxItems: 10,\n isEmptyItem: (item: any) => {\n return !item.name || item.name.trim() === '';\n },\n itemConfig: {\n name: {\n type: 'text',\n placeholder: 'Bucket name',\n required: true\n }\n }\n }\n },\n layout: ['categories'],\n validate: (formData: any) => {\n const errors: { [key: string]: string } = {};\n\n // Check for duplicate category names\n if (formData.categories && Array.isArray(formData.categories)) {\n const categories = formData.categories.filter(\n (item: any) => item?.name && item.name.trim() !== ''\n );\n\n // Ensure minimum buckets\n if (categories.length < 2) {\n errors.categories = 'At least 2 buckets are required for random split';\n }\n\n // Find all categories that have duplicates (case-insensitive)\n const duplicateCategories = [];\n const lowerCaseMap = new Map();\n\n // First pass: map lowercase names to all original cases\n categories.forEach((category) => {\n const lowerName = category.name.trim().toLowerCase();\n if (!lowerCaseMap.has(lowerName)) {\n lowerCaseMap.set(lowerName, []);\n }\n lowerCaseMap.get(lowerName).push(category.name.trim());\n });\n\n // Second pass: collect all names that appear more than once\n lowerCaseMap.forEach((originalNames) => {\n if (originalNames.length > 1) {\n duplicateCategories.push(...originalNames);\n }\n });\n\n if (duplicateCategories.length > 0) {\n const uniqueDuplicates = [...new Set(duplicateCategories)];\n errors.categories = `Duplicate bucket names found: ${uniqueDuplicates.join(\n ', '\n )}`;\n }\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors\n };\n },\n toFormData: (node: Node) => {\n // Extract categories from the existing node structure\n const categories =\n node.router?.categories?.map((cat) => ({ name: cat.name })) || [];\n\n return {\n uuid: node.uuid,\n categories: categories\n };\n },\n fromFormData: (formData: any, originalNode: Node): Node => {\n // Get user categories\n const userCategories = (formData.categories || [])\n .filter((item: any) => item?.name?.trim())\n .map((item: any) => item.name.trim());\n\n // Create router and exits using existing data when possible\n const existingCategories = originalNode.router?.categories || [];\n const existingExits = originalNode.exits || [];\n\n const { router, exits } = createRandomRouter(\n userCategories,\n existingCategories,\n existingExits\n );\n\n // Return the complete node\n return {\n uuid: originalNode.uuid,\n actions: originalNode.actions || [],\n router: router,\n exits: exits\n };\n },\n router: {\n type: 'random'\n }\n};\n"]}
1
+ {"version":3,"file":"split_by_random.js","sourceRoot":"","sources":["../../../../src/flow/nodes/split_by_random.ts"],"names":[],"mappings":"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","sourcesContent":["import { COLORS, NodeConfig } from '../types';\nimport { Node, Category, Exit } from '../../store/flow-definition.d';\nimport { generateUUID } from '../../utils';\n\n// Helper function to create a random router with categories\nconst createRandomRouter = (\n userCategories: string[],\n existingCategories: Category[] = [],\n existingExits: Exit[] = []\n) => {\n const categories: Category[] = [];\n const exits: Exit[] = [];\n\n // Create categories and exits for user-defined buckets\n userCategories.forEach((categoryName) => {\n // Try to find existing category by name\n const existingCategory = existingCategories.find(\n (cat) => cat.name === categoryName\n );\n const existingExit = existingCategory\n ? existingExits.find((exit) => exit.uuid === existingCategory.exit_uuid)\n : null;\n\n const exitUuid = existingExit?.uuid || generateUUID();\n const categoryUuid = existingCategory?.uuid || generateUUID();\n\n categories.push({\n uuid: categoryUuid,\n name: categoryName,\n exit_uuid: exitUuid\n });\n\n exits.push({\n uuid: exitUuid,\n destination_uuid: existingExit?.destination_uuid || null\n });\n });\n\n return {\n router: {\n type: 'random' as const,\n categories: categories\n },\n exits: exits\n };\n};\n\nexport const split_by_random: NodeConfig = {\n type: 'split_by_random',\n name: 'Split by Random',\n color: COLORS.split,\n form: {\n categories: {\n type: 'array',\n label: 'Buckets',\n helpText: 'Define the buckets to randomly split contacts into',\n required: true,\n itemLabel: 'Bucket',\n sortable: true,\n minItems: 2,\n maxItems: 10,\n isEmptyItem: (item: any) => {\n return !item.name || item.name.trim() === '';\n },\n itemConfig: {\n name: {\n type: 'text',\n placeholder: 'Bucket name',\n required: true\n }\n }\n }\n },\n layout: ['categories'],\n validate: (formData: any) => {\n const errors: { [key: string]: string } = {};\n\n // Check for duplicate category names\n if (formData.categories && Array.isArray(formData.categories)) {\n const categories = formData.categories.filter(\n (item: any) => item?.name && item.name.trim() !== ''\n );\n\n // Ensure minimum buckets\n if (categories.length < 2) {\n errors.categories = 'At least 2 buckets are required for random split';\n }\n\n // Find all categories that have duplicates (case-insensitive)\n const duplicateCategories = [];\n const lowerCaseMap = new Map();\n\n // First pass: map lowercase names to all original cases\n categories.forEach((category) => {\n const lowerName = category.name.trim().toLowerCase();\n if (!lowerCaseMap.has(lowerName)) {\n lowerCaseMap.set(lowerName, []);\n }\n lowerCaseMap.get(lowerName).push(category.name.trim());\n });\n\n // Second pass: collect all names that appear more than once\n lowerCaseMap.forEach((originalNames) => {\n if (originalNames.length > 1) {\n duplicateCategories.push(...originalNames);\n }\n });\n\n if (duplicateCategories.length > 0) {\n const uniqueDuplicates = [...new Set(duplicateCategories)];\n errors.categories = `Duplicate bucket names found: ${uniqueDuplicates.join(\n ', '\n )}`;\n }\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors\n };\n },\n toFormData: (node: Node) => {\n // Extract categories from the existing node structure\n const categories =\n node.router?.categories?.map((cat) => ({ name: cat.name })) || [];\n\n return {\n uuid: node.uuid,\n categories: categories\n };\n },\n fromFormData: (formData: any, originalNode: Node): Node => {\n // Get user categories\n const userCategories = (formData.categories || [])\n .filter((item: any) => item?.name?.trim())\n .map((item: any) => item.name.trim());\n\n // Create router and exits using existing data when possible\n const existingCategories = originalNode.router?.categories || [];\n const existingExits = originalNode.exits || [];\n\n const { router, exits } = createRandomRouter(\n userCategories,\n existingCategories,\n existingExits\n );\n\n // Return the complete node\n return {\n uuid: originalNode.uuid,\n actions: originalNode.actions || [],\n router: router,\n exits: exits\n };\n },\n router: {\n type: 'random'\n }\n};\n"]}
@@ -21,16 +21,115 @@ const TIMEOUT_OPTIONS = [
21
21
  { value: '259200', name: '3 days' },
22
22
  { value: '604800', name: '1 week' }
23
23
  ];
24
+ // Helper function to check if a category is a system category
25
+ const isSystemCategory = (categoryName) => {
26
+ return ['No Response', 'Other', 'All Responses', 'Timeout'].includes(categoryName);
27
+ };
28
+ // Helper function to check if a UUID belongs to a system category
29
+ const isSystemCategoryUuid = (uuid, categories) => {
30
+ const category = categories.find((cat) => cat.uuid === uuid);
31
+ return category ? isSystemCategory(category.name) : false;
32
+ };
33
+ // Helper function to generate default category name based on operator and operands
34
+ const generateDefaultCategoryName = (operator, value1, value2) => {
35
+ const operatorConfig = getOperatorConfig(operator);
36
+ if (!operatorConfig)
37
+ return '';
38
+ // Fixed category names (no operands)
39
+ if (operatorConfig.operands === 0) {
40
+ return operatorConfig.categoryName || '';
41
+ }
42
+ // Dynamic category names based on operands
43
+ const cleanValue1 = (value1 || '').trim();
44
+ const cleanValue2 = (value2 || '').trim();
45
+ // Helper to capitalize first letter
46
+ const capitalize = (str) => {
47
+ if (!str)
48
+ return '';
49
+ return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
50
+ };
51
+ // Handle different operator types
52
+ switch (operator) {
53
+ // Word/phrase operators - capitalize first letter of value
54
+ case 'has_any_word':
55
+ case 'has_all_words':
56
+ case 'has_phrase':
57
+ case 'has_only_phrase':
58
+ case 'has_beginning':
59
+ return cleanValue1 ? capitalize(cleanValue1) : '';
60
+ // Pattern operators - show as-is
61
+ case 'has_pattern':
62
+ return cleanValue1;
63
+ // Number comparison operators - include symbol
64
+ case 'has_number_eq':
65
+ return cleanValue1 ? `= ${cleanValue1}` : '';
66
+ case 'has_number_lt':
67
+ return cleanValue1 ? `< ${cleanValue1}` : '';
68
+ case 'has_number_lte':
69
+ return cleanValue1 ? `≤ ${cleanValue1}` : '';
70
+ case 'has_number_gt':
71
+ return cleanValue1 ? `> ${cleanValue1}` : '';
72
+ case 'has_number_gte':
73
+ return cleanValue1 ? `≥ ${cleanValue1}` : '';
74
+ // Number between - range format
75
+ case 'has_number_between':
76
+ if (cleanValue1 && cleanValue2) {
77
+ return `${cleanValue1} - ${cleanValue2}`;
78
+ }
79
+ return '';
80
+ // Date operators - format with relative expressions
81
+ case 'has_date_lt':
82
+ case 'has_date_lte':
83
+ if (cleanValue1) {
84
+ // Parse relative date expression (e.g., "today + 5" or "today - 3")
85
+ const match = cleanValue1.match(/^(today)\s*([+-])\s*(\d+)$/i);
86
+ if (match) {
87
+ const [, base, operator, days] = match;
88
+ const dayWord = days === '1' ? 'day' : 'days';
89
+ return `Before ${base} ${operator} ${days} ${dayWord}`;
90
+ }
91
+ // Fallback for other date formats
92
+ return `Before ${cleanValue1}`;
93
+ }
94
+ return '';
95
+ case 'has_date_gt':
96
+ case 'has_date_gte':
97
+ if (cleanValue1) {
98
+ // Parse relative date expression
99
+ const match = cleanValue1.match(/^(today)\s*([+-])\s*(\d+)$/i);
100
+ if (match) {
101
+ const [, base, operator, days] = match;
102
+ const dayWord = days === '1' ? 'day' : 'days';
103
+ return `After ${base} ${operator} ${days} ${dayWord}`;
104
+ }
105
+ // Fallback for other date formats
106
+ return `After ${cleanValue1}`;
107
+ }
108
+ return '';
109
+ case 'has_date_eq':
110
+ if (cleanValue1) {
111
+ // Parse relative date expression
112
+ const match = cleanValue1.match(/^(today)\s*([+-])\s*(\d+)$/i);
113
+ if (match) {
114
+ const [, base, operator, days] = match;
115
+ const dayWord = days === '1' ? 'day' : 'days';
116
+ return `${base} ${operator} ${days} ${dayWord}`;
117
+ }
118
+ return cleanValue1;
119
+ }
120
+ return '';
121
+ default:
122
+ // Fallback - capitalize first value
123
+ return cleanValue1 ? capitalize(cleanValue1) : '';
124
+ }
125
+ };
24
126
  // Helper function to create a wait_for_response router with user rules
25
127
  const createWaitForResponseRouter = (userRules, existingCategories = [], existingExits = [], existingCases = []) => {
26
- var _a;
27
128
  const categories = [];
28
129
  const exits = [];
29
130
  const cases = [];
30
131
  // Filter existing categories to get only user-defined rules (exclude system categories)
31
- const existingUserCategories = existingCategories.filter((cat) => cat.name !== 'No Response' &&
32
- cat.name !== 'Other' &&
33
- cat.name !== 'Timeout');
132
+ const existingUserCategories = existingCategories.filter((cat) => !isSystemCategory(cat.name));
34
133
  // Track categories as we create them (case-insensitive lookup)
35
134
  const createdCategories = new Map();
36
135
  // Process rules in their original order to preserve rule order
@@ -46,13 +145,24 @@ const createWaitForResponseRouter = (userRules, existingCategories = [], existin
46
145
  const categoryCreationOrder = Array.from(createdCategories.keys()).length;
47
146
  if (!existingCategory &&
48
147
  categoryCreationOrder < existingUserCategories.length) {
49
- existingCategory = existingUserCategories[categoryCreationOrder];
148
+ const candidateCategory = existingUserCategories[categoryCreationOrder];
149
+ // Double-check that this candidate is not a system category UUID
150
+ if (candidateCategory &&
151
+ !isSystemCategoryUuid(candidateCategory.uuid, existingCategories)) {
152
+ existingCategory = candidateCategory;
153
+ }
50
154
  }
51
155
  const existingExit = existingCategory
52
156
  ? existingExits.find((exit) => exit.uuid === existingCategory.exit_uuid)
53
157
  : null;
54
- const exitUuid = (existingExit === null || existingExit === void 0 ? void 0 : existingExit.uuid) || generateUUID();
55
- const categoryUuid = (existingCategory === null || existingCategory === void 0 ? void 0 : existingCategory.uuid) || generateUUID();
158
+ // Generate UUIDs, ensuring we don't reuse system category UUIDs
159
+ let exitUuid = (existingExit === null || existingExit === void 0 ? void 0 : existingExit.uuid) || generateUUID();
160
+ let categoryUuid = (existingCategory === null || existingCategory === void 0 ? void 0 : existingCategory.uuid) || generateUUID();
161
+ // Additional safety check: if somehow we got a system category UUID, generate new ones
162
+ if (isSystemCategoryUuid(categoryUuid, existingCategories)) {
163
+ categoryUuid = generateUUID();
164
+ exitUuid = generateUUID();
165
+ }
56
166
  categoryInfo = {
57
167
  uuid: categoryUuid,
58
168
  name: categoryName,
@@ -128,39 +238,46 @@ const createWaitForResponseRouter = (userRules, existingCategories = [], existin
128
238
  category_uuid: categoryInfo.uuid
129
239
  });
130
240
  });
131
- // Preserve existing timeout categories like "No Response"
132
- existingCategories.forEach((category) => {
133
- if (category.name === 'No Response' || category.name === 'Timeout') {
134
- const existingExit = existingExits.find((exit) => exit.uuid === category.exit_uuid);
135
- if (existingExit) {
136
- categories.push(category);
137
- exits.push(existingExit);
138
- }
139
- }
241
+ // Add default category (always present)
242
+ // Name is "Other" if there are user rules, "All Responses" if there are no user rules
243
+ const defaultCategoryName = userRules.length > 0 ? 'Other' : 'All Responses';
244
+ // Try to find existing default category by name (prefer exact match)
245
+ let existingDefaultCategory = existingCategories.find((cat) => cat.name === defaultCategoryName);
246
+ // If no exact match, try to find the other possible default category name
247
+ if (!existingDefaultCategory) {
248
+ const alternateName = userRules.length > 0 ? 'All Responses' : 'Other';
249
+ existingDefaultCategory = existingCategories.find((cat) => cat.name === alternateName);
250
+ }
251
+ const existingDefaultExit = existingDefaultCategory
252
+ ? existingExits.find((exit) => exit.uuid === existingDefaultCategory.exit_uuid)
253
+ : null;
254
+ const defaultExitUuid = (existingDefaultExit === null || existingDefaultExit === void 0 ? void 0 : existingDefaultExit.uuid) || generateUUID();
255
+ const defaultCategoryUuid = (existingDefaultCategory === null || existingDefaultCategory === void 0 ? void 0 : existingDefaultCategory.uuid) || generateUUID();
256
+ categories.push({
257
+ uuid: defaultCategoryUuid,
258
+ name: defaultCategoryName,
259
+ exit_uuid: defaultExitUuid
140
260
  });
141
- // Add "Other" category (default) only if there are user rules
142
- if (userRules.length > 0) {
143
- const existingOtherCategory = existingCategories.find((cat) => cat.name === 'Other');
144
- const existingOtherExit = existingOtherCategory
145
- ? existingExits.find((exit) => exit.uuid === existingOtherCategory.exit_uuid)
146
- : null;
147
- const otherExitUuid = (existingOtherExit === null || existingOtherExit === void 0 ? void 0 : existingOtherExit.uuid) || generateUUID();
148
- const otherCategoryUuid = (existingOtherCategory === null || existingOtherCategory === void 0 ? void 0 : existingOtherCategory.uuid) || generateUUID();
149
- categories.push({
150
- uuid: otherCategoryUuid,
151
- name: 'Other',
152
- exit_uuid: otherExitUuid
153
- });
154
- exits.push({
155
- uuid: otherExitUuid,
156
- destination_uuid: (existingOtherExit === null || existingOtherExit === void 0 ? void 0 : existingOtherExit.destination_uuid) || null
157
- });
261
+ exits.push({
262
+ uuid: defaultExitUuid,
263
+ destination_uuid: (existingDefaultExit === null || existingDefaultExit === void 0 ? void 0 : existingDefaultExit.destination_uuid) || null
264
+ });
265
+ // Add "No Response" category last (if it exists in the original)
266
+ const existingNoResponseCategory = existingCategories.find((cat) => cat.name === 'No Response' || cat.name === 'Timeout');
267
+ if (existingNoResponseCategory) {
268
+ const existingNoResponseExit = existingExits.find((exit) => exit.uuid === existingNoResponseCategory.exit_uuid);
269
+ if (existingNoResponseExit) {
270
+ categories.push(existingNoResponseCategory);
271
+ exits.push(existingNoResponseExit);
272
+ }
158
273
  }
274
+ // Find the default category (either "Other" or "All Responses")
275
+ const defaultCategory = categories.find((cat) => cat.name === 'Other' || cat.name === 'All Responses');
159
276
  return {
160
277
  router: {
161
278
  type: 'switch',
162
279
  categories: categories,
163
- default_category_uuid: (_a = categories.find((cat) => cat.name === 'Other')) === null || _a === void 0 ? void 0 : _a.uuid,
280
+ default_category_uuid: defaultCategory === null || defaultCategory === void 0 ? void 0 : defaultCategory.uuid,
164
281
  operand: '@input.text',
165
282
  cases: cases
166
283
  },
@@ -225,6 +342,55 @@ export const wait_for_response = {
225
342
  // No value required for this operator
226
343
  return false;
227
344
  },
345
+ onItemChange: (itemIndex, field, value, allItems) => {
346
+ const updatedItems = [...allItems];
347
+ const item = { ...updatedItems[itemIndex] };
348
+ // Helper to get operator value from various formats
349
+ const getOperatorValue = (operator) => {
350
+ if (typeof operator === 'string') {
351
+ return operator.trim();
352
+ }
353
+ else if (Array.isArray(operator) && operator.length > 0) {
354
+ const firstOperator = operator[0];
355
+ if (firstOperator &&
356
+ typeof firstOperator === 'object' &&
357
+ firstOperator.value) {
358
+ return firstOperator.value.trim();
359
+ }
360
+ }
361
+ else if (operator &&
362
+ typeof operator === 'object' &&
363
+ operator.value) {
364
+ return operator.value.trim();
365
+ }
366
+ return '';
367
+ };
368
+ // Update the changed field
369
+ item[field] = value;
370
+ // Get operator values (before and after the change)
371
+ const oldItem = allItems[itemIndex] || {};
372
+ const oldOperatorValue = field === 'operator'
373
+ ? getOperatorValue(oldItem.operator)
374
+ : getOperatorValue(item.operator);
375
+ const newOperatorValue = getOperatorValue(item.operator);
376
+ // Calculate what the default category name should be before the change
377
+ const oldDefaultCategory = generateDefaultCategoryName(oldOperatorValue, field === 'value1' ? oldItem.value1 : item.value1, field === 'value2' ? oldItem.value2 : item.value2);
378
+ // Calculate what the new default category name should be after the change
379
+ const newDefaultCategory = generateDefaultCategoryName(newOperatorValue, item.value1, item.value2);
380
+ // Determine if we should auto-update the category
381
+ const shouldUpdateCategory =
382
+ // Category is empty
383
+ !item.category ||
384
+ item.category.trim() === '' ||
385
+ // Category matches the old default (user hasn't customized it)
386
+ item.category === oldDefaultCategory;
387
+ // Auto-populate or update category if conditions are met
388
+ if (shouldUpdateCategory && newDefaultCategory) {
389
+ item.category = newDefaultCategory;
390
+ }
391
+ updatedItems[itemIndex] = item;
392
+ return updatedItems;
393
+ },
228
394
  itemConfig: {
229
395
  operator: {
230
396
  type: 'select',
@@ -361,10 +527,8 @@ export const wait_for_response = {
361
527
  node.router.cases.forEach((case_) => {
362
528
  // Find the category for this case
363
529
  const category = node.router.categories.find((cat) => cat.uuid === case_.category_uuid);
364
- // Skip timeout/system categories like "No Response"
365
- if (category &&
366
- category.name !== 'No Response' &&
367
- category.name !== 'Other') {
530
+ // Skip system categories
531
+ if (category && !isSystemCategory(category.name)) {
368
532
  // Handle different operator types
369
533
  const operatorConfig = getOperatorConfig(case_.type);
370
534
  const operatorDisplayName = operatorConfig
@@ -416,7 +580,7 @@ export const wait_for_response = {
416
580
  };
417
581
  },
418
582
  fromFormData: (formData, originalNode) => {
419
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
583
+ var _a, _b, _c, _d, _e, _f, _g, _h;
420
584
  // Helper function to get operator value from various formats
421
585
  const getOperatorValue = (operator) => {
422
586
  if (typeof operator === 'string') {
@@ -490,14 +654,46 @@ export const wait_for_response = {
490
654
  });
491
655
  // If no user rules, clear cases but preserve other router config
492
656
  if (userRules.length === 0) {
657
+ // Get existing router data for preservation
658
+ let existingCategories = ((_a = originalNode.router) === null || _a === void 0 ? void 0 : _a.categories) || [];
659
+ const existingExits = [...(originalNode.exits || [])]; // Create a copy to avoid extensibility issues
660
+ // Handle timeout: ensure "No Response" category exists if timeout is enabled,
661
+ // or remove it if timeout is disabled
662
+ if (formData.timeout_enabled) {
663
+ let noResponseCategory = existingCategories.find((cat) => cat.name === 'No Response');
664
+ if (!noResponseCategory) {
665
+ // Create new "No Response" category and exit
666
+ const noResponseExitUuid = generateUUID();
667
+ noResponseCategory = {
668
+ uuid: generateUUID(),
669
+ name: 'No Response',
670
+ exit_uuid: noResponseExitUuid
671
+ };
672
+ // Add to existing categories for processing
673
+ existingCategories = [...existingCategories, noResponseCategory];
674
+ // Add corresponding exit if it doesn't exist
675
+ if (!existingExits.find((exit) => exit.uuid === noResponseExitUuid)) {
676
+ existingExits.push({
677
+ uuid: noResponseExitUuid,
678
+ destination_uuid: null
679
+ });
680
+ }
681
+ }
682
+ }
683
+ else {
684
+ // If timeout is disabled, remove "No Response" category from existing categories
685
+ existingCategories = existingCategories.filter((cat) => cat.name !== 'No Response');
686
+ }
687
+ // Create router with "All Responses" as default category
688
+ // This will now properly handle the "No Response" category if it exists
689
+ const { router: noRulesRouter, exits: noRulesExits } = createWaitForResponseRouter([], // No user rules
690
+ existingCategories, existingExits, [] // No cases
691
+ );
493
692
  const router = {
494
- ...originalNode.router,
495
- result_name: formData.result_name || 'response'
693
+ ...noRulesRouter,
694
+ result_name: formData.result_name || 'response',
695
+ cases: [] // Clear all cases when no rules
496
696
  };
497
- // Only set cases to empty if the original node had cases
498
- if (((_a = originalNode.router) === null || _a === void 0 ? void 0 : _a.cases) !== undefined) {
499
- router.cases = []; // Clear all cases when no rules
500
- }
501
697
  // Build wait configuration based on form data
502
698
  const waitConfig = {
503
699
  type: 'msg'
@@ -532,33 +728,26 @@ export const wait_for_response = {
532
728
  if (isNaN(timeoutSeconds) || timeoutSeconds <= 0) {
533
729
  timeoutSeconds = 300; // Default to 5 minutes
534
730
  }
535
- // Find or create the "No Response" category
536
- let noResponseCategory = (_c = (_b = originalNode.router) === null || _b === void 0 ? void 0 : _b.categories) === null || _c === void 0 ? void 0 : _c.find((cat) => cat.name === 'No Response');
537
- if (!noResponseCategory) {
538
- noResponseCategory = {
539
- uuid: generateUUID(),
540
- name: 'No Response',
541
- exit_uuid: generateUUID()
731
+ // Find the "No Response" category (should exist now)
732
+ const noResponseCategory = router.categories.find((cat) => cat.name === 'No Response');
733
+ if (noResponseCategory) {
734
+ waitConfig.timeout = {
735
+ seconds: timeoutSeconds,
736
+ category_uuid: noResponseCategory.uuid
542
737
  };
543
- // Add to router categories
544
- router.categories = router.categories || [];
545
- router.categories.push(noResponseCategory);
546
738
  }
547
- waitConfig.timeout = {
548
- seconds: timeoutSeconds,
549
- category_uuid: noResponseCategory.uuid
550
- };
551
739
  }
552
740
  router.wait = waitConfig;
553
741
  return {
554
742
  ...originalNode,
555
- router
743
+ router,
744
+ exits: noRulesExits
556
745
  };
557
746
  }
558
747
  // Get existing router data for preservation
559
- const existingCategories = ((_d = originalNode.router) === null || _d === void 0 ? void 0 : _d.categories) || [];
748
+ const existingCategories = ((_b = originalNode.router) === null || _b === void 0 ? void 0 : _b.categories) || [];
560
749
  const existingExits = originalNode.exits || [];
561
- const existingCases = ((_e = originalNode.router) === null || _e === void 0 ? void 0 : _e.cases) || [];
750
+ const existingCases = ((_c = originalNode.router) === null || _c === void 0 ? void 0 : _c.cases) || [];
562
751
  // Create router and exits using existing data when possible
563
752
  const { router, exits } = createWaitForResponseRouter(userRules, existingCategories, existingExits, existingCases);
564
753
  // Build final router with wait configuration and result_name
@@ -584,7 +773,7 @@ export const wait_for_response = {
584
773
  }
585
774
  }
586
775
  // Find or create the "No Response" category
587
- const existingNoResponseCategory = (_g = (_f = originalNode.router) === null || _f === void 0 ? void 0 : _f.categories) === null || _g === void 0 ? void 0 : _g.find((cat) => cat.name === 'No Response');
776
+ const existingNoResponseCategory = (_e = (_d = originalNode.router) === null || _d === void 0 ? void 0 : _d.categories) === null || _e === void 0 ? void 0 : _e.find((cat) => cat.name === 'No Response');
588
777
  const noResponseCategory = existingNoResponseCategory || {
589
778
  uuid: generateUUID(),
590
779
  name: 'No Response',
@@ -595,7 +784,7 @@ export const wait_for_response = {
595
784
  category_uuid: noResponseCategory.uuid
596
785
  };
597
786
  // Ensure No Response category and exit exist
598
- if (!((_h = router.categories) === null || _h === void 0 ? void 0 : _h.some((cat) => cat.name === 'No Response'))) {
787
+ if (!((_f = router.categories) === null || _f === void 0 ? void 0 : _f.some((cat) => cat.name === 'No Response'))) {
599
788
  router.categories = router.categories || [];
600
789
  router.categories.push(noResponseCategory);
601
790
  // Add corresponding exit if it doesn't exist
@@ -603,7 +792,7 @@ export const wait_for_response = {
603
792
  const noResponseExit = {
604
793
  uuid: noResponseCategory.exit_uuid,
605
794
  destination_uuid: (existingNoResponseCategory === null || existingNoResponseCategory === void 0 ? void 0 : existingNoResponseCategory.exit_uuid)
606
- ? ((_k = (_j = originalNode.exits) === null || _j === void 0 ? void 0 : _j.find((exit) => exit.uuid === existingNoResponseCategory.exit_uuid)) === null || _k === void 0 ? void 0 : _k.destination_uuid) || null
795
+ ? ((_h = (_g = originalNode.exits) === null || _g === void 0 ? void 0 : _g.find((exit) => exit.uuid === existingNoResponseCategory.exit_uuid)) === null || _h === void 0 ? void 0 : _h.destination_uuid) || null
607
796
  : null
608
797
  };
609
798
  exits.push(noResponseExit);