@nyaruka/temba-components 0.131.1 → 0.131.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (427) hide show
  1. package/.github/workflows/publish.yml +4 -1
  2. package/CHANGELOG.md +61 -1
  3. package/demo/data/flows/food-order.json +2 -2
  4. package/demo/data/flows/sample-flow.json +74 -125
  5. package/dist/static/svg/index.svg +1 -1
  6. package/dist/temba-components.js +1155 -618
  7. package/dist/temba-components.js.map +1 -1
  8. package/out-tsc/src/Icons.js +4 -1
  9. package/out-tsc/src/Icons.js.map +1 -1
  10. package/out-tsc/src/events.js.map +1 -1
  11. package/out-tsc/src/flow/CanvasMenu.js +200 -0
  12. package/out-tsc/src/flow/CanvasMenu.js.map +1 -0
  13. package/out-tsc/src/flow/CanvasNode.js +327 -19
  14. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  15. package/out-tsc/src/flow/Editor.js +562 -66
  16. package/out-tsc/src/flow/Editor.js.map +1 -1
  17. package/out-tsc/src/flow/NodeEditor.js +240 -93
  18. package/out-tsc/src/flow/NodeEditor.js.map +1 -1
  19. package/out-tsc/src/flow/NodeTypeSelector.js +499 -0
  20. package/out-tsc/src/flow/NodeTypeSelector.js.map +1 -0
  21. package/out-tsc/src/flow/actions/add_contact_groups.js +3 -3
  22. package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
  23. package/out-tsc/src/flow/actions/add_contact_urn.js +62 -4
  24. package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
  25. package/out-tsc/src/flow/actions/add_input_labels.js +3 -3
  26. package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
  27. package/out-tsc/src/flow/actions/play_audio.js +2 -2
  28. package/out-tsc/src/flow/actions/play_audio.js.map +1 -1
  29. package/out-tsc/src/flow/actions/remove_contact_groups.js +6 -5
  30. package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -1
  31. package/out-tsc/src/flow/actions/request_optin.js +2 -2
  32. package/out-tsc/src/flow/actions/request_optin.js.map +1 -1
  33. package/out-tsc/src/flow/actions/say_msg.js +2 -2
  34. package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
  35. package/out-tsc/src/flow/actions/send_broadcast.js +76 -23
  36. package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
  37. package/out-tsc/src/flow/actions/send_email.js +4 -5
  38. package/out-tsc/src/flow/actions/send_email.js.map +1 -1
  39. package/out-tsc/src/flow/actions/send_msg.js +9 -19
  40. package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
  41. package/out-tsc/src/flow/actions/set_contact_channel.js +5 -9
  42. package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
  43. package/out-tsc/src/flow/actions/set_contact_field.js +19 -20
  44. package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
  45. package/out-tsc/src/flow/actions/set_contact_language.js +2 -2
  46. package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
  47. package/out-tsc/src/flow/actions/set_contact_name.js +2 -12
  48. package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
  49. package/out-tsc/src/flow/actions/set_contact_status.js +2 -2
  50. package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
  51. package/out-tsc/src/flow/actions/set_run_result.js +3 -3
  52. package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
  53. package/out-tsc/src/flow/actions/start_session.js +180 -6
  54. package/out-tsc/src/flow/actions/start_session.js.map +1 -1
  55. package/out-tsc/src/flow/config.js +11 -15
  56. package/out-tsc/src/flow/config.js.map +1 -1
  57. package/out-tsc/src/flow/currencies.js +45 -0
  58. package/out-tsc/src/flow/currencies.js.map +1 -0
  59. package/out-tsc/src/flow/nodes/shared-rules.js +257 -0
  60. package/out-tsc/src/flow/nodes/shared-rules.js.map +1 -0
  61. package/out-tsc/src/flow/nodes/shared.js +17 -0
  62. package/out-tsc/src/flow/nodes/shared.js.map +1 -0
  63. package/out-tsc/src/flow/nodes/split_by_airtime.js +205 -5
  64. package/out-tsc/src/flow/nodes/split_by_airtime.js.map +1 -1
  65. package/out-tsc/src/flow/nodes/split_by_contact_field.js +147 -3
  66. package/out-tsc/src/flow/nodes/split_by_contact_field.js.map +1 -1
  67. package/out-tsc/src/flow/nodes/split_by_expression.js +68 -2
  68. package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -1
  69. package/out-tsc/src/flow/nodes/split_by_groups.js +12 -9
  70. package/out-tsc/src/flow/nodes/split_by_groups.js.map +1 -1
  71. package/out-tsc/src/flow/nodes/split_by_intent.js +7 -0
  72. package/out-tsc/src/flow/nodes/split_by_intent.js.map +1 -0
  73. package/out-tsc/src/flow/nodes/split_by_llm.js +3 -2
  74. package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -1
  75. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +2 -2
  76. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
  77. package/out-tsc/src/flow/nodes/split_by_random.js +3 -3
  78. package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
  79. package/out-tsc/src/flow/nodes/split_by_resthook.js +108 -0
  80. package/out-tsc/src/flow/nodes/split_by_resthook.js.map +1 -0
  81. package/out-tsc/src/flow/nodes/split_by_run_result.js +206 -3
  82. package/out-tsc/src/flow/nodes/split_by_run_result.js.map +1 -1
  83. package/out-tsc/src/flow/nodes/split_by_scheme.js +153 -2
  84. package/out-tsc/src/flow/nodes/split_by_scheme.js.map +1 -1
  85. package/out-tsc/src/flow/nodes/split_by_subflow.js +6 -4
  86. package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
  87. package/out-tsc/src/flow/nodes/split_by_ticket.js +3 -2
  88. package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -1
  89. package/out-tsc/src/flow/nodes/split_by_webhook.js +3 -2
  90. package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
  91. package/out-tsc/src/flow/nodes/wait_for_audio.js +2 -2
  92. package/out-tsc/src/flow/nodes/wait_for_audio.js.map +1 -1
  93. package/out-tsc/src/flow/nodes/wait_for_digits.js +2 -2
  94. package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
  95. package/out-tsc/src/flow/nodes/wait_for_image.js +2 -2
  96. package/out-tsc/src/flow/nodes/wait_for_image.js.map +1 -1
  97. package/out-tsc/src/flow/nodes/wait_for_location.js +2 -2
  98. package/out-tsc/src/flow/nodes/wait_for_location.js.map +1 -1
  99. package/out-tsc/src/flow/nodes/wait_for_menu.js +2 -2
  100. package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -1
  101. package/out-tsc/src/flow/nodes/wait_for_response.js +32 -567
  102. package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
  103. package/out-tsc/src/flow/nodes/wait_for_video.js +2 -2
  104. package/out-tsc/src/flow/nodes/wait_for_video.js.map +1 -1
  105. package/out-tsc/src/flow/types.js +71 -12
  106. package/out-tsc/src/flow/types.js.map +1 -1
  107. package/out-tsc/src/flow/utils.js +101 -14
  108. package/out-tsc/src/flow/utils.js.map +1 -1
  109. package/out-tsc/src/form/FieldRenderer.js +2 -4
  110. package/out-tsc/src/form/FieldRenderer.js.map +1 -1
  111. package/out-tsc/src/interfaces.js +3 -0
  112. package/out-tsc/src/interfaces.js.map +1 -1
  113. package/out-tsc/src/list/SortableList.js +98 -33
  114. package/out-tsc/src/list/SortableList.js.map +1 -1
  115. package/out-tsc/src/live/ContactChat.js +15 -18
  116. package/out-tsc/src/live/ContactChat.js.map +1 -1
  117. package/out-tsc/src/store/AppState.js +53 -0
  118. package/out-tsc/src/store/AppState.js.map +1 -1
  119. package/out-tsc/src/utils.js +254 -13
  120. package/out-tsc/src/utils.js.map +1 -1
  121. package/out-tsc/temba-modules.js +4 -0
  122. package/out-tsc/temba-modules.js.map +1 -1
  123. package/out-tsc/test/ActionHelper.js +3 -3
  124. package/out-tsc/test/ActionHelper.js.map +1 -1
  125. package/out-tsc/test/NodeHelper.js +6 -3
  126. package/out-tsc/test/NodeHelper.js.map +1 -1
  127. package/out-tsc/test/actions/add_contact_urn.test.js +202 -0
  128. package/out-tsc/test/actions/add_contact_urn.test.js.map +1 -0
  129. package/out-tsc/test/actions/send_broadcast.test.js +148 -0
  130. package/out-tsc/test/actions/send_broadcast.test.js.map +1 -0
  131. package/out-tsc/test/actions/send_email.test.js +17 -23
  132. package/out-tsc/test/actions/send_email.test.js.map +1 -1
  133. package/out-tsc/test/actions/send_msg.test.js +33 -15
  134. package/out-tsc/test/actions/send_msg.test.js.map +1 -1
  135. package/out-tsc/test/actions/start_session.test.js +116 -0
  136. package/out-tsc/test/actions/start_session.test.js.map +1 -0
  137. package/out-tsc/test/nodes/split_by_airtime.test.js +604 -0
  138. package/out-tsc/test/nodes/split_by_airtime.test.js.map +1 -0
  139. package/out-tsc/test/nodes/split_by_contact_field.test.js +387 -0
  140. package/out-tsc/test/nodes/split_by_contact_field.test.js.map +1 -0
  141. package/out-tsc/test/nodes/split_by_expression.test.js +614 -0
  142. package/out-tsc/test/nodes/split_by_expression.test.js.map +1 -0
  143. package/out-tsc/test/nodes/split_by_random.test.js +3 -3
  144. package/out-tsc/test/nodes/split_by_random.test.js.map +1 -1
  145. package/out-tsc/test/nodes/split_by_resthook.test.js +337 -0
  146. package/out-tsc/test/nodes/split_by_resthook.test.js.map +1 -0
  147. package/out-tsc/test/nodes/split_by_run_result.test.js +920 -0
  148. package/out-tsc/test/nodes/split_by_run_result.test.js.map +1 -0
  149. package/out-tsc/test/nodes/split_by_scheme.test.js +399 -0
  150. package/out-tsc/test/nodes/split_by_scheme.test.js.map +1 -0
  151. package/out-tsc/test/nodes/split_by_subflow.test.js +333 -0
  152. package/out-tsc/test/nodes/split_by_subflow.test.js.map +1 -0
  153. package/out-tsc/test/nodes/wait_for_digits.test.js +2 -2
  154. package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -1
  155. package/out-tsc/test/nodes/wait_for_response.test.js +2 -1
  156. package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -1
  157. package/out-tsc/test/temba-action-drag-between-nodes.test.js +252 -0
  158. package/out-tsc/test/temba-action-drag-between-nodes.test.js.map +1 -0
  159. package/out-tsc/test/temba-canvas-menu.test.js +122 -0
  160. package/out-tsc/test/temba-canvas-menu.test.js.map +1 -0
  161. package/out-tsc/test/temba-flow-editor-node.test.js +85 -2
  162. package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
  163. package/out-tsc/test/temba-flow-editor.test.js +7 -8
  164. package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
  165. package/out-tsc/test/temba-node-editor.test.js +3 -1
  166. package/out-tsc/test/temba-node-editor.test.js.map +1 -1
  167. package/out-tsc/test/temba-node-type-selector.test.js +115 -0
  168. package/out-tsc/test/temba-node-type-selector.test.js.map +1 -0
  169. package/out-tsc/test/temba-omnibox.test.js +2 -1
  170. package/out-tsc/test/temba-omnibox.test.js.map +1 -1
  171. package/out-tsc/test/temba-sortable-list.test.js +51 -0
  172. package/out-tsc/test/temba-sortable-list.test.js.map +1 -1
  173. package/out-tsc/test/temba-utils-index.test.js +1 -27
  174. package/out-tsc/test/temba-utils-index.test.js.map +1 -1
  175. package/out-tsc/test/utils.test.js +2 -0
  176. package/out-tsc/test/utils.test.js.map +1 -1
  177. package/package.json +2 -1
  178. package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
  179. package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
  180. package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
  181. package/screenshots/truth/actions/add_contact_groups/editor/multiple-groups.png +0 -0
  182. package/screenshots/truth/actions/add_contact_groups/editor/single-group.png +0 -0
  183. package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
  184. package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
  185. package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
  186. package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
  187. package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
  188. package/screenshots/truth/actions/add_contact_urn/editor/expression-facebook.png +0 -0
  189. package/screenshots/truth/actions/add_contact_urn/editor/expression-phone.png +0 -0
  190. package/screenshots/truth/actions/add_contact_urn/editor/facebook-id.png +0 -0
  191. package/screenshots/truth/actions/add_contact_urn/editor/instagram-handle.png +0 -0
  192. package/screenshots/truth/actions/add_contact_urn/editor/line-id.png +0 -0
  193. package/screenshots/truth/actions/add_contact_urn/editor/phone-number.png +0 -0
  194. package/screenshots/truth/actions/add_contact_urn/editor/telegram-id.png +0 -0
  195. package/screenshots/truth/actions/add_contact_urn/editor/viber-id.png +0 -0
  196. package/screenshots/truth/actions/add_contact_urn/editor/wechat-id.png +0 -0
  197. package/screenshots/truth/actions/add_contact_urn/editor/whatsapp.png +0 -0
  198. package/screenshots/truth/actions/add_contact_urn/render/expression-facebook.png +0 -0
  199. package/screenshots/truth/actions/add_contact_urn/render/expression-phone.png +0 -0
  200. package/screenshots/truth/actions/add_contact_urn/render/facebook-id.png +0 -0
  201. package/screenshots/truth/actions/add_contact_urn/render/instagram-handle.png +0 -0
  202. package/screenshots/truth/actions/add_contact_urn/render/line-id.png +0 -0
  203. package/screenshots/truth/actions/add_contact_urn/render/phone-number.png +0 -0
  204. package/screenshots/truth/actions/add_contact_urn/render/telegram-id.png +0 -0
  205. package/screenshots/truth/actions/add_contact_urn/render/viber-id.png +0 -0
  206. package/screenshots/truth/actions/add_contact_urn/render/wechat-id.png +0 -0
  207. package/screenshots/truth/actions/add_contact_urn/render/whatsapp.png +0 -0
  208. package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
  209. package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
  210. package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
  211. package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
  212. package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
  213. package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
  214. package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
  215. package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
  216. package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
  217. package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
  218. package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
  219. package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
  220. package/screenshots/truth/actions/send_broadcast/editor/contacts-only.png +0 -0
  221. package/screenshots/truth/actions/send_broadcast/editor/groups-and-contacts.png +0 -0
  222. package/screenshots/truth/actions/send_broadcast/editor/groups-only.png +0 -0
  223. package/screenshots/truth/actions/send_broadcast/editor/many-groups.png +0 -0
  224. package/screenshots/truth/actions/send_broadcast/editor/multiline-text.png +0 -0
  225. package/screenshots/truth/actions/send_broadcast/editor/with-attachments.png +0 -0
  226. package/screenshots/truth/actions/send_broadcast/render/contacts-only.png +0 -0
  227. package/screenshots/truth/actions/send_broadcast/render/groups-and-contacts.png +0 -0
  228. package/screenshots/truth/actions/send_broadcast/render/groups-only.png +0 -0
  229. package/screenshots/truth/actions/send_broadcast/render/many-groups.png +0 -0
  230. package/screenshots/truth/actions/send_broadcast/render/multiline-text.png +0 -0
  231. package/screenshots/truth/actions/send_broadcast/render/with-attachments.png +0 -0
  232. package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
  233. package/screenshots/truth/actions/send_email/editor/empty-body.png +0 -0
  234. package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
  235. package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
  236. package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
  237. package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
  238. package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
  239. package/screenshots/truth/actions/send_email/editor/with-expressions.png +0 -0
  240. package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
  241. package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
  242. package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
  243. package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
  244. package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
  245. package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
  246. package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
  247. package/screenshots/truth/actions/send_email/render/with-expressions.png +0 -0
  248. package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
  249. package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
  250. package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
  251. package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
  252. package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
  253. package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
  254. package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
  255. package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
  256. package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
  257. package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
  258. package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
  259. package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
  260. package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
  261. package/screenshots/truth/actions/send_msg/render/text-without-quick-replies.png +0 -0
  262. package/screenshots/truth/actions/start_session/editor/contact-query.png +0 -0
  263. package/screenshots/truth/actions/start_session/editor/contacts-only.png +0 -0
  264. package/screenshots/truth/actions/start_session/editor/create-contact.png +0 -0
  265. package/screenshots/truth/actions/start_session/editor/groups-and-contacts.png +0 -0
  266. package/screenshots/truth/actions/start_session/editor/groups-only.png +0 -0
  267. package/screenshots/truth/actions/start_session/editor/many-recipients.png +0 -0
  268. package/screenshots/truth/actions/start_session/render/contact-query.png +0 -0
  269. package/screenshots/truth/actions/start_session/render/contacts-only.png +0 -0
  270. package/screenshots/truth/actions/start_session/render/create-contact.png +0 -0
  271. package/screenshots/truth/actions/start_session/render/groups-and-contacts.png +0 -0
  272. package/screenshots/truth/actions/start_session/render/groups-only.png +0 -0
  273. package/screenshots/truth/actions/start_session/render/many-recipients.png +0 -0
  274. package/screenshots/truth/canvas-menu/open.png +0 -0
  275. package/screenshots/truth/editor/router.png +0 -0
  276. package/screenshots/truth/editor/wait.png +0 -0
  277. package/screenshots/truth/list/fields-dragging.png +0 -0
  278. package/screenshots/truth/list/sortable-dragging.png +0 -0
  279. package/screenshots/truth/node-type-selector/action-mode.png +0 -0
  280. package/screenshots/truth/node-type-selector/split-mode.png +0 -0
  281. package/screenshots/truth/nodes/split_by_llm/editor/information-extraction.png +0 -0
  282. package/screenshots/truth/nodes/split_by_llm/editor/sentiment-analysis.png +0 -0
  283. package/screenshots/truth/nodes/split_by_llm/editor/summarization.png +0 -0
  284. package/screenshots/truth/nodes/split_by_llm/editor/translation-task.png +0 -0
  285. package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
  286. package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
  287. package/screenshots/truth/nodes/split_by_llm/render/summarization.png +0 -0
  288. package/screenshots/truth/nodes/split_by_llm/render/translation-task.png +0 -0
  289. package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
  290. package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
  291. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  292. package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
  293. package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
  294. package/screenshots/truth/nodes/split_by_llm_categorize/render/basic-categorization.png +0 -0
  295. package/screenshots/truth/nodes/split_by_llm_categorize/render/custom-input-and-result-name.png +0 -0
  296. package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
  297. package/screenshots/truth/nodes/split_by_llm_categorize/render/many-categories.png +0 -0
  298. package/screenshots/truth/nodes/split_by_llm_categorize/render/minimal-categories.png +0 -0
  299. package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
  300. package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
  301. package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
  302. package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
  303. package/screenshots/truth/nodes/split_by_random/render/ab-test-multiple-variants.png +0 -0
  304. package/screenshots/truth/nodes/split_by_random/render/sampling-split.png +0 -0
  305. package/screenshots/truth/nodes/split_by_random/render/three-way-split.png +0 -0
  306. package/screenshots/truth/nodes/split_by_random/render/two-bucket-split.png +0 -0
  307. package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
  308. package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
  309. package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
  310. package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
  311. package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
  312. package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
  313. package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
  314. package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
  315. package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
  316. package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
  317. package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
  318. package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
  319. package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
  320. package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
  321. package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
  322. package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
  323. package/src/Icons.ts +4 -1
  324. package/src/events.ts +2 -6
  325. package/src/flow/CanvasMenu.ts +217 -0
  326. package/src/flow/CanvasNode.ts +408 -10
  327. package/src/flow/Editor.ts +683 -44
  328. package/src/flow/NodeEditor.ts +304 -125
  329. package/src/flow/NodeTypeSelector.ts +592 -0
  330. package/src/flow/actions/add_contact_groups.ts +4 -4
  331. package/src/flow/actions/add_contact_urn.ts +76 -4
  332. package/src/flow/actions/add_input_labels.ts +4 -4
  333. package/src/flow/actions/play_audio.ts +2 -2
  334. package/src/flow/actions/remove_contact_groups.ts +14 -6
  335. package/src/flow/actions/request_optin.ts +2 -2
  336. package/src/flow/actions/say_msg.ts +2 -2
  337. package/src/flow/actions/send_broadcast.ts +85 -23
  338. package/src/flow/actions/send_email.ts +10 -6
  339. package/src/flow/actions/send_msg.ts +22 -32
  340. package/src/flow/actions/set_contact_channel.ts +5 -11
  341. package/src/flow/actions/set_contact_field.ts +20 -25
  342. package/src/flow/actions/set_contact_language.ts +9 -4
  343. package/src/flow/actions/set_contact_name.ts +3 -15
  344. package/src/flow/actions/set_contact_status.ts +3 -3
  345. package/src/flow/actions/set_run_result.ts +4 -4
  346. package/src/flow/actions/start_session.ts +208 -6
  347. package/src/flow/config.ts +13 -15
  348. package/src/flow/currencies.ts +51 -0
  349. package/src/flow/nodes/shared-rules.ts +301 -0
  350. package/src/flow/nodes/shared.ts +18 -0
  351. package/src/flow/nodes/split_by_airtime.ts +238 -5
  352. package/src/flow/nodes/split_by_contact_field.ts +185 -3
  353. package/src/flow/nodes/split_by_expression.ts +94 -2
  354. package/src/flow/nodes/split_by_groups.ts +15 -10
  355. package/src/flow/nodes/split_by_intent.ts +7 -0
  356. package/src/flow/nodes/split_by_llm.ts +4 -3
  357. package/src/flow/nodes/split_by_llm_categorize.ts +4 -4
  358. package/src/flow/nodes/split_by_random.ts +5 -5
  359. package/src/flow/nodes/split_by_resthook.ts +130 -0
  360. package/src/flow/nodes/split_by_run_result.ts +249 -3
  361. package/src/flow/nodes/split_by_scheme.ts +192 -2
  362. package/src/flow/nodes/split_by_subflow.ts +6 -4
  363. package/src/flow/nodes/split_by_ticket.ts +4 -3
  364. package/src/flow/nodes/split_by_webhook.ts +6 -5
  365. package/src/flow/nodes/wait_for_audio.ts +2 -2
  366. package/src/flow/nodes/wait_for_digits.ts +2 -2
  367. package/src/flow/nodes/wait_for_image.ts +2 -2
  368. package/src/flow/nodes/wait_for_location.ts +2 -2
  369. package/src/flow/nodes/wait_for_menu.ts +2 -2
  370. package/src/flow/nodes/wait_for_response.ts +48 -679
  371. package/src/flow/nodes/wait_for_video.ts +2 -2
  372. package/src/flow/types.ts +109 -23
  373. package/src/flow/utils.ts +108 -14
  374. package/src/form/FieldRenderer.ts +2 -4
  375. package/src/interfaces.ts +3 -0
  376. package/src/list/SortableList.ts +109 -34
  377. package/src/live/ContactChat.ts +15 -18
  378. package/src/store/AppState.ts +69 -0
  379. package/src/store/flow-definition.d.ts +2 -5
  380. package/src/utils.ts +332 -12
  381. package/static/api/channels.json +46 -0
  382. package/static/api/resthooks.json +31 -0
  383. package/static/svg/index.svg +1 -1
  384. package/static/svg/work/traced/lightning-02.svg +1 -0
  385. package/static/svg/work/used/lightning-02.svg +3 -0
  386. package/temba-modules.ts +4 -0
  387. package/test/ActionHelper.ts +3 -3
  388. package/test/NodeHelper.ts +6 -3
  389. package/test/actions/add_contact_urn.test.ts +287 -0
  390. package/test/actions/send_broadcast.test.ts +190 -0
  391. package/test/actions/send_email.test.ts +17 -23
  392. package/test/actions/send_msg.test.ts +39 -15
  393. package/test/actions/start_session.test.ts +151 -0
  394. package/test/nodes/split_by_airtime.test.ts +673 -0
  395. package/test/nodes/split_by_contact_field.test.ts +451 -0
  396. package/test/nodes/split_by_expression.test.ts +751 -0
  397. package/test/nodes/split_by_random.test.ts +3 -3
  398. package/test/nodes/split_by_resthook.test.ts +398 -0
  399. package/test/nodes/split_by_run_result.test.ts +1109 -0
  400. package/test/nodes/split_by_scheme.test.ts +486 -0
  401. package/test/nodes/split_by_subflow.test.ts +381 -0
  402. package/test/nodes/wait_for_digits.test.ts +2 -2
  403. package/test/nodes/wait_for_response.test.ts +2 -1
  404. package/test/temba-action-drag-between-nodes.test.ts +301 -0
  405. package/test/temba-canvas-menu.test.ts +156 -0
  406. package/test/temba-flow-editor-node.test.ts +102 -2
  407. package/test/temba-flow-editor.test.ts +7 -8
  408. package/test/temba-node-editor.test.ts +3 -1
  409. package/test/temba-node-type-selector.test.ts +152 -0
  410. package/test/temba-omnibox.test.ts +2 -1
  411. package/test/temba-sortable-list.test.ts +69 -0
  412. package/test/temba-utils-index.test.ts +0 -35
  413. package/test/utils.test.ts +2 -0
  414. package/test-assets/contacts/history.json +14 -20
  415. package/web-dev-server.config.mjs +3 -1
  416. package/out-tsc/src/flow/actions/call_classifier.js +0 -11
  417. package/out-tsc/src/flow/actions/call_classifier.js.map +0 -1
  418. package/out-tsc/src/flow/actions/call_resthook.js +0 -11
  419. package/out-tsc/src/flow/actions/call_resthook.js.map +0 -1
  420. package/out-tsc/src/flow/actions/split_by_expression_example.js +0 -77
  421. package/out-tsc/src/flow/actions/split_by_expression_example.js.map +0 -1
  422. package/out-tsc/src/flow/actions/transfer_airtime.js +0 -11
  423. package/out-tsc/src/flow/actions/transfer_airtime.js.map +0 -1
  424. package/src/flow/actions/call_classifier.ts +0 -12
  425. package/src/flow/actions/call_resthook.ts +0 -12
  426. package/src/flow/actions/split_by_expression_example.ts +0 -88
  427. package/src/flow/actions/transfer_airtime.ts +0 -12
@@ -0,0 +1,451 @@
1
+ import { expect } from '@open-wc/testing';
2
+ import { split_by_contact_field } from '../../src/flow/nodes/split_by_contact_field';
3
+ import { Node } from '../../src/store/flow-definition';
4
+
5
+ describe('split_by_contact_field', () => {
6
+ it('should have correct configuration', () => {
7
+ expect(split_by_contact_field.type).to.equal('split_by_contact_field');
8
+ expect(split_by_contact_field.name).to.equal('Split by Contact Field');
9
+ expect(split_by_contact_field.dialogSize).to.equal('large');
10
+ });
11
+
12
+ it('should have field select configuration', () => {
13
+ const fieldConfig = split_by_contact_field.form!.field as any;
14
+ expect(fieldConfig).to.exist;
15
+ expect(fieldConfig.type).to.equal('select');
16
+ expect(fieldConfig.required).to.be.true;
17
+ expect(fieldConfig.searchable).to.be.true;
18
+ expect(fieldConfig.endpoint).to.equal('/api/v2/fields.json');
19
+ });
20
+
21
+ it('should provide system properties and URN schemes as options', () => {
22
+ const fieldConfig = split_by_contact_field.form!.field as any;
23
+ const options = fieldConfig.options;
24
+ expect(options).to.exist;
25
+ expect(Array.isArray(options)).to.be.true;
26
+
27
+ // Should have 4 system properties + multiple URN schemes
28
+ expect(options.length).to.be.greaterThan(4);
29
+
30
+ // Check that system properties are present
31
+ const names = options.map((opt: any) => opt.name);
32
+ expect(names).to.include('Name');
33
+ expect(names).to.include('Language');
34
+ expect(names).to.include('Status');
35
+ expect(names).to.include('Channel');
36
+
37
+ // Check that some URN schemes are present
38
+ expect(names).to.include('WhatsApp Number');
39
+ expect(names).to.include('Facebook ID');
40
+ });
41
+
42
+ it('should have rules configuration', () => {
43
+ expect(split_by_contact_field.form!.rules).to.exist;
44
+ expect(split_by_contact_field.form!.rules.type).to.equal('array');
45
+ });
46
+
47
+ it('should have result_name configuration', () => {
48
+ expect(split_by_contact_field.form!.result_name).to.exist;
49
+ expect(split_by_contact_field.form!.result_name.type).to.equal('text');
50
+ expect(split_by_contact_field.form!.result_name.required).to.be.false;
51
+ });
52
+
53
+ it('should validate that field is required', () => {
54
+ const result = split_by_contact_field.validate!({});
55
+ expect(result.valid).to.be.false;
56
+ expect(result.errors).to.have.property('field');
57
+ });
58
+
59
+ it('should validate successfully with a field', () => {
60
+ const result = split_by_contact_field.validate!({
61
+ field: [{ id: 'name', name: 'Name', type: 'property' }]
62
+ });
63
+ expect(result.valid).to.be.true;
64
+ expect(Object.keys(result.errors).length).to.equal(0);
65
+ });
66
+
67
+ it('should transform from flow definition to form data for system property', () => {
68
+ const node: Node = {
69
+ uuid: 'test-node-uuid',
70
+ actions: [],
71
+ router: {
72
+ type: 'switch',
73
+ cases: [
74
+ {
75
+ uuid: 'case-1',
76
+ type: 'has_text',
77
+ arguments: ['John'],
78
+ category_uuid: 'cat-1'
79
+ }
80
+ ],
81
+ categories: [
82
+ { uuid: 'cat-1', name: 'John', exit_uuid: 'exit-1' },
83
+ { uuid: 'cat-other', name: 'Other', exit_uuid: 'exit-other' }
84
+ ],
85
+ default_category_uuid: 'cat-other',
86
+ operand: '@contact.name',
87
+ result_name: 'contact_name'
88
+ },
89
+ exits: [
90
+ { uuid: 'exit-1', destination_uuid: null },
91
+ { uuid: 'exit-other', destination_uuid: null }
92
+ ]
93
+ };
94
+
95
+ const nodeUI = {
96
+ type: 'split_by_contact_field',
97
+ position: { left: 0, top: 0 },
98
+ config: {
99
+ operand: {
100
+ id: 'name',
101
+ name: 'Name',
102
+ type: 'property'
103
+ }
104
+ }
105
+ };
106
+
107
+ const formData = split_by_contact_field.toFormData!(node, nodeUI);
108
+
109
+ expect(formData.uuid).to.equal('test-node-uuid');
110
+ expect(formData.field).to.have.lengthOf(1);
111
+ expect(formData.field[0].id).to.equal('name');
112
+ expect(formData.field[0].name).to.equal('Name');
113
+ expect(formData.field[0].type).to.equal('property');
114
+ expect(formData.result_name).to.equal('contact_name');
115
+ });
116
+
117
+ it('should transform from flow definition to form data for custom field', () => {
118
+ const node: Node = {
119
+ uuid: 'test-node-uuid',
120
+ actions: [],
121
+ router: {
122
+ type: 'switch',
123
+ cases: [
124
+ {
125
+ uuid: 'case-1',
126
+ type: 'has_text',
127
+ arguments: ['red'],
128
+ category_uuid: 'cat-1'
129
+ }
130
+ ],
131
+ categories: [
132
+ { uuid: 'cat-1', name: 'Red', exit_uuid: 'exit-1' },
133
+ { uuid: 'cat-other', name: 'Other', exit_uuid: 'exit-other' }
134
+ ],
135
+ default_category_uuid: 'cat-other',
136
+ operand: '@fields.favorite_color',
137
+ result_name: ''
138
+ },
139
+ exits: [
140
+ { uuid: 'exit-1', destination_uuid: null },
141
+ { uuid: 'exit-other', destination_uuid: null }
142
+ ]
143
+ };
144
+
145
+ const nodeUI = {
146
+ type: 'split_by_contact_field',
147
+ position: { left: 0, top: 0 },
148
+ config: {
149
+ operand: {
150
+ key: 'favorite_color',
151
+ id: 'favorite_color',
152
+ name: 'Favorite Color',
153
+ type: 'field'
154
+ }
155
+ }
156
+ };
157
+
158
+ const formData = split_by_contact_field.toFormData!(node, nodeUI);
159
+
160
+ expect(formData.uuid).to.equal('test-node-uuid');
161
+ expect(formData.field).to.have.lengthOf(1);
162
+ expect(formData.field[0].key).to.equal('favorite_color');
163
+ expect(formData.field[0].id).to.equal('favorite_color');
164
+ expect(formData.field[0].name).to.equal('Favorite Color');
165
+ expect(formData.field[0].type).to.equal('field');
166
+ expect(formData.result_name).to.equal('');
167
+ });
168
+
169
+ it('should transform from form data to flow definition for system property', () => {
170
+ const originalNode: Node = {
171
+ uuid: 'test-node-uuid',
172
+ actions: [],
173
+ router: {
174
+ type: 'switch',
175
+ cases: [],
176
+ categories: [],
177
+ default_category_uuid: '',
178
+ operand: '@input.text'
179
+ },
180
+ exits: []
181
+ };
182
+
183
+ const formData = {
184
+ uuid: 'test-node-uuid',
185
+ field: [{ id: 'language', name: 'Language', type: 'property' }],
186
+ rules: [
187
+ {
188
+ operator: 'has_only_phrase', // Requires 1 operand
189
+ value1: 'eng',
190
+ value2: '',
191
+ category: 'English'
192
+ }
193
+ ],
194
+ result_name: 'user_language'
195
+ };
196
+
197
+ const updatedNode = split_by_contact_field.fromFormData!(
198
+ formData,
199
+ originalNode
200
+ );
201
+
202
+ expect(updatedNode.router!.operand).to.equal('@contact.language');
203
+ expect(updatedNode.router!.result_name).to.equal('user_language');
204
+
205
+ // Should have at least an "Other" category from createRulesRouter
206
+ expect(updatedNode.router!.categories.length).to.be.greaterThan(0);
207
+ expect(updatedNode.exits.length).to.be.greaterThan(0);
208
+
209
+ // Check if the case was created (it should be if the rule is valid)
210
+ expect(updatedNode.router!.cases.length).to.be.greaterThan(0);
211
+ expect(updatedNode.router!.cases[0].type).to.equal('has_only_phrase');
212
+ expect(updatedNode.router!.cases[0].arguments).to.deep.equal(['eng']);
213
+ });
214
+
215
+ it('should transform from form data to flow definition for custom field', () => {
216
+ const originalNode: Node = {
217
+ uuid: 'test-node-uuid',
218
+ actions: [],
219
+ router: {
220
+ type: 'switch',
221
+ cases: [],
222
+ categories: [],
223
+ default_category_uuid: '',
224
+ operand: '@input.text'
225
+ },
226
+ exits: []
227
+ };
228
+
229
+ const formData = {
230
+ uuid: 'test-node-uuid',
231
+ field: [{ key: 'age', name: 'Age', type: 'field' }],
232
+ rules: [
233
+ {
234
+ operator: { value: 'has_number_gte', name: 'has a number >= to' },
235
+ value1: '18',
236
+ value2: '',
237
+ category: 'Adult'
238
+ }
239
+ ],
240
+ result_name: ''
241
+ };
242
+
243
+ const updatedNode = split_by_contact_field.fromFormData!(
244
+ formData,
245
+ originalNode
246
+ );
247
+
248
+ expect(updatedNode.router!.operand).to.equal('@fields.age');
249
+ expect(updatedNode.router!.result_name).to.be.undefined;
250
+ expect(updatedNode.router!.cases.length).to.equal(1);
251
+ expect(updatedNode.router!.cases[0].type).to.equal('has_number_gte');
252
+ });
253
+
254
+ it('should preserve existing exits when updating', () => {
255
+ const originalNode: Node = {
256
+ uuid: 'test-node-uuid',
257
+ actions: [],
258
+ router: {
259
+ type: 'switch',
260
+ cases: [
261
+ {
262
+ uuid: 'case-1',
263
+ type: 'has_text',
264
+ arguments: ['red'],
265
+ category_uuid: 'cat-1'
266
+ }
267
+ ],
268
+ categories: [
269
+ { uuid: 'cat-1', name: 'Red', exit_uuid: 'exit-1' },
270
+ { uuid: 'cat-other', name: 'Other', exit_uuid: 'exit-other' }
271
+ ],
272
+ default_category_uuid: 'cat-other',
273
+ operand: '@fields.color'
274
+ },
275
+ exits: [
276
+ { uuid: 'exit-1', destination_uuid: 'next-node-uuid' },
277
+ { uuid: 'exit-other', destination_uuid: null }
278
+ ]
279
+ };
280
+
281
+ const formData = {
282
+ uuid: 'test-node-uuid',
283
+ field: [{ key: 'color', name: 'Color', type: 'field' }],
284
+ rules: [
285
+ {
286
+ operator: { value: 'has_text', name: 'has some text' },
287
+ value1: 'blue',
288
+ value2: '',
289
+ category: 'Blue'
290
+ }
291
+ ],
292
+ result_name: ''
293
+ };
294
+
295
+ const updatedNode = split_by_contact_field.fromFormData!(
296
+ formData,
297
+ originalNode
298
+ );
299
+
300
+ // Should have at least 2 exits (Blue and Other)
301
+ expect(updatedNode.exits.length).to.be.at.least(2);
302
+
303
+ // Check that the operand is correct
304
+ expect(updatedNode.router!.operand).to.equal('@fields.color');
305
+ });
306
+
307
+ it('should generate UI config from form data for system property', () => {
308
+ const formData = {
309
+ field: [{ id: 'language', name: 'Language', type: 'property' }],
310
+ rules: [],
311
+ result_name: ''
312
+ };
313
+
314
+ const uiConfig = split_by_contact_field.toUIConfig!(formData);
315
+
316
+ expect(uiConfig.operand).to.exist;
317
+ expect(uiConfig.operand.id).to.equal('language');
318
+ expect(uiConfig.operand.name).to.equal('Language');
319
+ expect(uiConfig.operand.type).to.equal('property');
320
+ });
321
+
322
+ it('should generate UI config from form data for custom field', () => {
323
+ const formData = {
324
+ field: [
325
+ {
326
+ key: 'favorite_color',
327
+ id: 'favorite_color',
328
+ name: 'Favorite Color',
329
+ type: 'field'
330
+ }
331
+ ],
332
+ rules: [],
333
+ result_name: ''
334
+ };
335
+
336
+ const uiConfig = split_by_contact_field.toUIConfig!(formData);
337
+
338
+ expect(uiConfig.operand).to.exist;
339
+ expect(uiConfig.operand.id).to.equal('favorite_color');
340
+ expect(uiConfig.operand.name).to.equal('Favorite Color');
341
+ expect(uiConfig.operand.type).to.equal('field');
342
+ });
343
+
344
+ it('should transform from form data to flow definition for URN scheme', () => {
345
+ const originalNode: Node = {
346
+ uuid: 'test-node-uuid',
347
+ actions: [],
348
+ router: {
349
+ type: 'switch',
350
+ cases: [],
351
+ categories: [],
352
+ default_category_uuid: '',
353
+ operand: '@input.text'
354
+ },
355
+ exits: []
356
+ };
357
+
358
+ const formData = {
359
+ uuid: 'test-node-uuid',
360
+ field: [{ value: 'facebook', name: 'Facebook', type: 'scheme' }],
361
+ rules: [
362
+ {
363
+ operator: { value: 'has_only_phrase', name: 'has only the phrase' },
364
+ value1: '123456789',
365
+ value2: '',
366
+ category: 'Valid Facebook ID'
367
+ }
368
+ ],
369
+ result_name: 'facebook_id'
370
+ };
371
+
372
+ const updatedNode = split_by_contact_field.fromFormData!(
373
+ formData,
374
+ originalNode
375
+ );
376
+
377
+ // The operand should be splitting on the Facebook URN path (the ID)
378
+ expect(updatedNode.router!.operand).to.equal(
379
+ '@(default(urn_parts(urns.facebook).path, ""))'
380
+ );
381
+ expect(updatedNode.router!.result_name).to.equal('facebook_id');
382
+ expect(updatedNode.router!.cases.length).to.equal(1);
383
+ expect(updatedNode.router!.cases[0].type).to.equal('has_only_phrase');
384
+ expect(updatedNode.router!.cases[0].arguments).to.deep.equal(['123456789']);
385
+ });
386
+
387
+ it('should transform from flow definition to form data for URN scheme', () => {
388
+ const node: Node = {
389
+ uuid: 'test-node-uuid',
390
+ actions: [],
391
+ router: {
392
+ type: 'switch',
393
+ cases: [
394
+ {
395
+ uuid: 'case-1',
396
+ type: 'has_only_phrase',
397
+ arguments: ['987654321'],
398
+ category_uuid: 'cat-1'
399
+ }
400
+ ],
401
+ categories: [
402
+ { uuid: 'cat-1', name: 'Valid WhatsApp', exit_uuid: 'exit-1' },
403
+ { uuid: 'cat-other', name: 'Other', exit_uuid: 'exit-other' }
404
+ ],
405
+ default_category_uuid: 'cat-other',
406
+ operand: '@(default(urn_parts(urns.whatsapp).path, ""))',
407
+ result_name: 'whatsapp_number'
408
+ },
409
+ exits: [
410
+ { uuid: 'exit-1', destination_uuid: null },
411
+ { uuid: 'exit-other', destination_uuid: null }
412
+ ]
413
+ };
414
+
415
+ const nodeUI = {
416
+ type: 'split_by_contact_field',
417
+ position: { left: 0, top: 0 },
418
+ config: {
419
+ operand: {
420
+ id: 'whatsapp',
421
+ name: 'WhatsApp',
422
+ type: 'scheme'
423
+ }
424
+ }
425
+ };
426
+
427
+ const formData = split_by_contact_field.toFormData!(node, nodeUI);
428
+
429
+ expect(formData.uuid).to.equal('test-node-uuid');
430
+ expect(formData.field).to.have.lengthOf(1);
431
+ expect(formData.field[0].id).to.equal('whatsapp');
432
+ expect(formData.field[0].name).to.equal('WhatsApp');
433
+ expect(formData.field[0].type).to.equal('scheme');
434
+ expect(formData.result_name).to.equal('whatsapp_number');
435
+ });
436
+
437
+ it('should generate UI config from form data for URN scheme', () => {
438
+ const formData = {
439
+ field: [{ value: 'facebook', name: 'Facebook', type: 'scheme' }],
440
+ rules: [],
441
+ result_name: ''
442
+ };
443
+
444
+ const uiConfig = split_by_contact_field.toUIConfig!(formData);
445
+
446
+ expect(uiConfig.operand).to.exist;
447
+ expect(uiConfig.operand.id).to.equal('facebook');
448
+ expect(uiConfig.operand.name).to.equal('Facebook');
449
+ expect(uiConfig.operand.type).to.equal('scheme');
450
+ });
451
+ });