@nyaruka/temba-components 0.139.0 → 0.141.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.
Files changed (385) hide show
  1. package/.github/workflows/cla.yml +1 -1
  2. package/.github/workflows/copilot-setup-steps.yml +6 -1
  3. package/.lintstagedrc.js +10 -0
  4. package/CHANGELOG.md +32 -0
  5. package/demo/data/flows/sample-flow.json +24 -0
  6. package/dist/locales/es.js +5 -5
  7. package/dist/locales/es.js.map +1 -1
  8. package/dist/locales/fr.js +5 -5
  9. package/dist/locales/fr.js.map +1 -1
  10. package/dist/locales/locale-codes.js +11 -2
  11. package/dist/locales/locale-codes.js.map +1 -1
  12. package/dist/locales/pt.js +5 -5
  13. package/dist/locales/pt.js.map +1 -1
  14. package/dist/temba-components.js +702 -338
  15. package/dist/temba-components.js.map +1 -1
  16. package/out-tsc/src/display/Chat.js +10 -7
  17. package/out-tsc/src/display/Chat.js.map +1 -1
  18. package/out-tsc/src/display/Dropdown.js +3 -1
  19. package/out-tsc/src/display/Dropdown.js.map +1 -1
  20. package/out-tsc/src/display/FloatingTab.js +4 -4
  21. package/out-tsc/src/display/FloatingTab.js.map +1 -1
  22. package/out-tsc/src/display/Thumbnail.js +163 -5
  23. package/out-tsc/src/display/Thumbnail.js.map +1 -1
  24. package/out-tsc/src/flow/CanvasNode.js +65 -23
  25. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  26. package/out-tsc/src/flow/Editor.js +369 -49
  27. package/out-tsc/src/flow/Editor.js.map +1 -1
  28. package/out-tsc/src/flow/NodeEditor.js +118 -10
  29. package/out-tsc/src/flow/NodeEditor.js.map +1 -1
  30. package/out-tsc/src/flow/Plumber.js +61 -14
  31. package/out-tsc/src/flow/Plumber.js.map +1 -1
  32. package/out-tsc/src/flow/StickyNote.js +13 -4
  33. package/out-tsc/src/flow/StickyNote.js.map +1 -1
  34. package/out-tsc/src/flow/actions/add_contact_groups.js +4 -1
  35. package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
  36. package/out-tsc/src/flow/actions/add_input_labels.js +4 -1
  37. package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
  38. package/out-tsc/src/flow/actions/audio-player.js +112 -0
  39. package/out-tsc/src/flow/actions/audio-player.js.map +1 -0
  40. package/out-tsc/src/flow/actions/enter_flow.js +43 -0
  41. package/out-tsc/src/flow/actions/enter_flow.js.map +1 -0
  42. package/out-tsc/src/flow/actions/play_audio.js +57 -4
  43. package/out-tsc/src/flow/actions/play_audio.js.map +1 -1
  44. package/out-tsc/src/flow/actions/remove_contact_groups.js +6 -1
  45. package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -1
  46. package/out-tsc/src/flow/actions/say_msg.js +86 -3
  47. package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
  48. package/out-tsc/src/flow/actions/send_broadcast.js +6 -2
  49. package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
  50. package/out-tsc/src/flow/actions/set_contact_channel.js +13 -0
  51. package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
  52. package/out-tsc/src/flow/actions/set_contact_status.js +7 -5
  53. package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
  54. package/out-tsc/src/flow/actions/start_session.js +10 -3
  55. package/out-tsc/src/flow/actions/start_session.js.map +1 -1
  56. package/out-tsc/src/flow/config.js +11 -3
  57. package/out-tsc/src/flow/config.js.map +1 -1
  58. package/out-tsc/src/flow/nodes/shared-rules.js +1 -1
  59. package/out-tsc/src/flow/nodes/shared-rules.js.map +1 -1
  60. package/out-tsc/src/flow/nodes/split_by_contact_field.js +18 -5
  61. package/out-tsc/src/flow/nodes/split_by_contact_field.js.map +1 -1
  62. package/out-tsc/src/flow/nodes/split_by_expression.js +1 -1
  63. package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -1
  64. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +0 -1
  65. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
  66. package/out-tsc/src/flow/nodes/split_by_random.js +0 -1
  67. package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
  68. package/out-tsc/src/flow/nodes/split_by_run_result.js +10 -4
  69. package/out-tsc/src/flow/nodes/split_by_run_result.js.map +1 -1
  70. package/out-tsc/src/flow/nodes/terminal.js +7 -0
  71. package/out-tsc/src/flow/nodes/terminal.js.map +1 -0
  72. package/out-tsc/src/flow/nodes/wait_for_audio.js +77 -0
  73. package/out-tsc/src/flow/nodes/wait_for_audio.js.map +1 -0
  74. package/out-tsc/src/flow/nodes/wait_for_dial.js +151 -0
  75. package/out-tsc/src/flow/nodes/wait_for_dial.js.map +1 -0
  76. package/out-tsc/src/flow/nodes/wait_for_digits.js +61 -1
  77. package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
  78. package/out-tsc/src/flow/nodes/wait_for_menu.js +173 -2
  79. package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -1
  80. package/out-tsc/src/flow/nodes/wait_for_response.js +1 -1
  81. package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
  82. package/out-tsc/src/flow/operators.js +21 -5
  83. package/out-tsc/src/flow/operators.js.map +1 -1
  84. package/out-tsc/src/flow/types.js.map +1 -1
  85. package/out-tsc/src/flow/utils.js +79 -3
  86. package/out-tsc/src/flow/utils.js.map +1 -1
  87. package/out-tsc/src/form/ArrayEditor.js +4 -2
  88. package/out-tsc/src/form/ArrayEditor.js.map +1 -1
  89. package/out-tsc/src/form/FieldRenderer.js +56 -0
  90. package/out-tsc/src/form/FieldRenderer.js.map +1 -1
  91. package/out-tsc/src/interfaces.js +1 -0
  92. package/out-tsc/src/interfaces.js.map +1 -1
  93. package/out-tsc/src/layout/Dialog.js +51 -7
  94. package/out-tsc/src/layout/Dialog.js.map +1 -1
  95. package/out-tsc/src/layout/Modax.js +20 -2
  96. package/out-tsc/src/layout/Modax.js.map +1 -1
  97. package/out-tsc/src/list/ContentMenu.js +14 -1
  98. package/out-tsc/src/list/ContentMenu.js.map +1 -1
  99. package/out-tsc/src/locales/es.js +5 -5
  100. package/out-tsc/src/locales/es.js.map +1 -1
  101. package/out-tsc/src/locales/fr.js +5 -5
  102. package/out-tsc/src/locales/fr.js.map +1 -1
  103. package/out-tsc/src/locales/locale-codes.js +11 -2
  104. package/out-tsc/src/locales/locale-codes.js.map +1 -1
  105. package/out-tsc/src/locales/pt.js +5 -5
  106. package/out-tsc/src/locales/pt.js.map +1 -1
  107. package/out-tsc/src/simulator/Simulator.js +21 -4
  108. package/out-tsc/src/simulator/Simulator.js.map +1 -1
  109. package/out-tsc/src/store/AppState.js +102 -3
  110. package/out-tsc/src/store/AppState.js.map +1 -1
  111. package/out-tsc/test/actions/add_contact_groups.test.js +35 -0
  112. package/out-tsc/test/actions/add_contact_groups.test.js.map +1 -1
  113. package/out-tsc/test/actions/add_input_labels.test.js +53 -0
  114. package/out-tsc/test/actions/add_input_labels.test.js.map +1 -0
  115. package/out-tsc/test/actions/enter_flow.test.js +71 -0
  116. package/out-tsc/test/actions/enter_flow.test.js.map +1 -0
  117. package/out-tsc/test/actions/play_audio.test.js +118 -0
  118. package/out-tsc/test/actions/play_audio.test.js.map +1 -0
  119. package/out-tsc/test/actions/remove_contact_groups.test.js +24 -0
  120. package/out-tsc/test/actions/remove_contact_groups.test.js.map +1 -1
  121. package/out-tsc/test/actions/say_msg.test.js +158 -0
  122. package/out-tsc/test/actions/say_msg.test.js.map +1 -0
  123. package/out-tsc/test/actions/send_broadcast.test.js +41 -0
  124. package/out-tsc/test/actions/send_broadcast.test.js.map +1 -1
  125. package/out-tsc/test/actions/set_contact_channel.test.js +67 -0
  126. package/out-tsc/test/actions/set_contact_channel.test.js.map +1 -0
  127. package/out-tsc/test/actions/set_contact_field.test.js +52 -0
  128. package/out-tsc/test/actions/set_contact_field.test.js.map +1 -0
  129. package/out-tsc/test/actions/set_contact_language.test.js +39 -0
  130. package/out-tsc/test/actions/set_contact_language.test.js.map +1 -0
  131. package/out-tsc/test/actions/set_contact_name.test.js +28 -0
  132. package/out-tsc/test/actions/set_contact_name.test.js.map +1 -0
  133. package/out-tsc/test/actions/set_contact_status.test.js +44 -0
  134. package/out-tsc/test/actions/set_contact_status.test.js.map +1 -0
  135. package/out-tsc/test/actions/set_run_result.test.js +47 -0
  136. package/out-tsc/test/actions/set_run_result.test.js.map +1 -0
  137. package/out-tsc/test/actions/start_session.test.js +76 -0
  138. package/out-tsc/test/actions/start_session.test.js.map +1 -1
  139. package/out-tsc/test/nodes/split_by_contact_field.test.js +50 -0
  140. package/out-tsc/test/nodes/split_by_contact_field.test.js.map +1 -1
  141. package/out-tsc/test/nodes/split_by_run_result.test.js +82 -0
  142. package/out-tsc/test/nodes/split_by_run_result.test.js.map +1 -1
  143. package/out-tsc/test/nodes/split_by_ticket.test.js +139 -0
  144. package/out-tsc/test/nodes/split_by_ticket.test.js.map +1 -0
  145. package/out-tsc/test/nodes/split_by_webhook.test.js +111 -0
  146. package/out-tsc/test/nodes/split_by_webhook.test.js.map +1 -0
  147. package/out-tsc/test/nodes/wait_for_audio.test.js +156 -0
  148. package/out-tsc/test/nodes/wait_for_audio.test.js.map +1 -0
  149. package/out-tsc/test/nodes/wait_for_dial.test.js +336 -0
  150. package/out-tsc/test/nodes/wait_for_dial.test.js.map +1 -0
  151. package/out-tsc/test/nodes/wait_for_digits.test.js +198 -84
  152. package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -1
  153. package/out-tsc/test/nodes/wait_for_menu.test.js +340 -0
  154. package/out-tsc/test/nodes/wait_for_menu.test.js.map +1 -0
  155. package/out-tsc/test/temba-flow-collision.test.js +261 -6
  156. package/out-tsc/test/temba-flow-collision.test.js.map +1 -1
  157. package/out-tsc/test/temba-flow-editor.test.js +187 -0
  158. package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
  159. package/out-tsc/test/temba-flow-plumber.test.js +19 -0
  160. package/out-tsc/test/temba-flow-plumber.test.js.map +1 -1
  161. package/out-tsc/test/temba-node-type-selector.test.js +6 -6
  162. package/out-tsc/test/temba-node-type-selector.test.js.map +1 -1
  163. package/out-tsc/test/temba-select.test.js +4 -1
  164. package/out-tsc/test/temba-select.test.js.map +1 -1
  165. package/out-tsc/test/utils.test.js +4 -2
  166. package/out-tsc/test/utils.test.js.map +1 -1
  167. package/package.json +3 -9
  168. package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
  169. package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
  170. package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
  171. package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
  172. package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
  173. package/screenshots/truth/actions/add_contact_urn/render/expression-facebook.png +0 -0
  174. package/screenshots/truth/actions/add_contact_urn/render/expression-phone.png +0 -0
  175. package/screenshots/truth/actions/add_contact_urn/render/facebook-id.png +0 -0
  176. package/screenshots/truth/actions/add_contact_urn/render/instagram-handle.png +0 -0
  177. package/screenshots/truth/actions/add_contact_urn/render/line-id.png +0 -0
  178. package/screenshots/truth/actions/add_contact_urn/render/phone-number.png +0 -0
  179. package/screenshots/truth/actions/add_contact_urn/render/telegram-id.png +0 -0
  180. package/screenshots/truth/actions/add_contact_urn/render/viber-id.png +0 -0
  181. package/screenshots/truth/actions/add_contact_urn/render/wechat-id.png +0 -0
  182. package/screenshots/truth/actions/add_contact_urn/render/whatsapp.png +0 -0
  183. package/screenshots/truth/actions/add_input_labels/editor/multiple-labels.png +0 -0
  184. package/screenshots/truth/actions/add_input_labels/editor/single-label.png +0 -0
  185. package/screenshots/truth/actions/add_input_labels/render/multiple-labels.png +0 -0
  186. package/screenshots/truth/actions/add_input_labels/render/single-label.png +0 -0
  187. package/screenshots/truth/actions/enter_flow/editor/basic-flow.png +0 -0
  188. package/screenshots/truth/actions/enter_flow/editor/long-flow-name.png +0 -0
  189. package/screenshots/truth/actions/enter_flow/render/basic-flow.png +0 -0
  190. package/screenshots/truth/actions/enter_flow/render/long-flow-name.png +0 -0
  191. package/screenshots/truth/actions/play_audio/editor/expression-url.png +0 -0
  192. package/screenshots/truth/actions/play_audio/editor/static-url.png +0 -0
  193. package/screenshots/truth/actions/play_audio/render/expression-url.png +0 -0
  194. package/screenshots/truth/actions/play_audio/render/static-url.png +0 -0
  195. package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
  196. package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
  197. package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
  198. package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
  199. package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
  200. package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
  201. package/screenshots/truth/actions/say_msg/editor/multiline-text.png +0 -0
  202. package/screenshots/truth/actions/say_msg/editor/simple-text.png +0 -0
  203. package/screenshots/truth/actions/say_msg/editor/text-with-audio-url.png +0 -0
  204. package/screenshots/truth/actions/say_msg/render/multiline-text.png +0 -0
  205. package/screenshots/truth/actions/say_msg/render/simple-text.png +0 -0
  206. package/screenshots/truth/actions/say_msg/render/text-with-audio-url.png +0 -0
  207. package/screenshots/truth/actions/send_broadcast/render/contacts-only.png +0 -0
  208. package/screenshots/truth/actions/send_broadcast/render/groups-and-contacts.png +0 -0
  209. package/screenshots/truth/actions/send_broadcast/render/groups-only.png +0 -0
  210. package/screenshots/truth/actions/send_broadcast/render/many-groups.png +0 -0
  211. package/screenshots/truth/actions/send_broadcast/render/multiline-text.png +0 -0
  212. package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
  213. package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
  214. package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
  215. package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
  216. package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
  217. package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
  218. package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
  219. package/screenshots/truth/actions/send_email/render/with-expressions.png +0 -0
  220. package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
  221. package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
  222. package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
  223. package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
  224. package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
  225. package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
  226. package/screenshots/truth/actions/send_msg/render/text-without-quick-replies.png +0 -0
  227. package/screenshots/truth/actions/set_contact_channel/editor/sms-channel.png +0 -0
  228. package/screenshots/truth/actions/set_contact_channel/editor/whatsapp-channel.png +0 -0
  229. package/screenshots/truth/actions/set_contact_channel/render/sms-channel.png +0 -0
  230. package/screenshots/truth/actions/set_contact_channel/render/whatsapp-channel.png +0 -0
  231. package/screenshots/truth/actions/set_contact_field/editor/clear-value.png +0 -0
  232. package/screenshots/truth/actions/set_contact_field/editor/set-value.png +0 -0
  233. package/screenshots/truth/actions/set_contact_field/render/clear-value.png +0 -0
  234. package/screenshots/truth/actions/set_contact_field/render/set-value.png +0 -0
  235. package/screenshots/truth/actions/set_contact_language/editor/english.png +0 -0
  236. package/screenshots/truth/actions/set_contact_language/editor/french.png +0 -0
  237. package/screenshots/truth/actions/set_contact_language/render/english.png +0 -0
  238. package/screenshots/truth/actions/set_contact_language/render/french.png +0 -0
  239. package/screenshots/truth/actions/set_contact_name/editor/expression-name.png +0 -0
  240. package/screenshots/truth/actions/set_contact_name/editor/static-name.png +0 -0
  241. package/screenshots/truth/actions/set_contact_name/render/expression-name.png +0 -0
  242. package/screenshots/truth/actions/set_contact_name/render/static-name.png +0 -0
  243. package/screenshots/truth/actions/set_contact_status/editor/active.png +0 -0
  244. package/screenshots/truth/actions/set_contact_status/editor/archived.png +0 -0
  245. package/screenshots/truth/actions/set_contact_status/editor/blocked.png +0 -0
  246. package/screenshots/truth/actions/set_contact_status/render/active.png +0 -0
  247. package/screenshots/truth/actions/set_contact_status/render/archived.png +0 -0
  248. package/screenshots/truth/actions/set_contact_status/render/blocked.png +0 -0
  249. package/screenshots/truth/actions/set_run_result/editor/expression-value.png +0 -0
  250. package/screenshots/truth/actions/set_run_result/editor/with-category.png +0 -0
  251. package/screenshots/truth/actions/set_run_result/render/expression-value.png +0 -0
  252. package/screenshots/truth/actions/set_run_result/render/with-category.png +0 -0
  253. package/screenshots/truth/actions/start_session/render/contact-query.png +0 -0
  254. package/screenshots/truth/actions/start_session/render/contacts-only.png +0 -0
  255. package/screenshots/truth/actions/start_session/render/create-contact.png +0 -0
  256. package/screenshots/truth/actions/start_session/render/groups-and-contacts.png +0 -0
  257. package/screenshots/truth/actions/start_session/render/groups-only.png +0 -0
  258. package/screenshots/truth/actions/start_session/render/many-recipients.png +0 -0
  259. package/screenshots/truth/editor/router.png +0 -0
  260. package/screenshots/truth/editor/wait.png +0 -0
  261. package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
  262. package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
  263. package/screenshots/truth/nodes/split_by_llm/render/summarization.png +0 -0
  264. package/screenshots/truth/nodes/split_by_llm/render/translation-task.png +0 -0
  265. package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
  266. package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
  267. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  268. package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
  269. package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
  270. package/screenshots/truth/nodes/split_by_llm_categorize/render/basic-categorization.png +0 -0
  271. package/screenshots/truth/nodes/split_by_llm_categorize/render/custom-input-and-result-name.png +0 -0
  272. package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
  273. package/screenshots/truth/nodes/split_by_llm_categorize/render/many-categories.png +0 -0
  274. package/screenshots/truth/nodes/split_by_llm_categorize/render/minimal-categories.png +0 -0
  275. package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
  276. package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
  277. package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
  278. package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
  279. package/screenshots/truth/nodes/split_by_random/render/ab-test-multiple-variants.png +0 -0
  280. package/screenshots/truth/nodes/split_by_random/render/sampling-split.png +0 -0
  281. package/screenshots/truth/nodes/split_by_random/render/three-way-split.png +0 -0
  282. package/screenshots/truth/nodes/split_by_random/render/two-bucket-split.png +0 -0
  283. package/screenshots/truth/nodes/wait_for_audio/editor/basic-audio-wait.png +0 -0
  284. package/screenshots/truth/nodes/wait_for_audio/render/basic-audio-wait.png +0 -0
  285. package/screenshots/truth/nodes/wait_for_dial/editor/basic-dial.png +0 -0
  286. package/screenshots/truth/nodes/wait_for_dial/editor/dial-with-limits.png +0 -0
  287. package/screenshots/truth/nodes/wait_for_dial/render/basic-dial.png +0 -0
  288. package/screenshots/truth/nodes/wait_for_dial/render/dial-with-limits.png +0 -0
  289. package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
  290. package/screenshots/truth/nodes/wait_for_digits/editor/digits-with-rules.png +0 -0
  291. package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
  292. package/screenshots/truth/nodes/wait_for_digits/render/digits-with-rules.png +0 -0
  293. package/screenshots/truth/nodes/wait_for_menu/editor/menu-with-digits.png +0 -0
  294. package/screenshots/truth/nodes/wait_for_menu/render/menu-with-digits.png +0 -0
  295. package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
  296. package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
  297. package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
  298. package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
  299. package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
  300. package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
  301. package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
  302. package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
  303. package/src/display/Chat.ts +13 -7
  304. package/src/display/Dropdown.ts +3 -1
  305. package/src/display/FloatingTab.ts +4 -4
  306. package/src/display/Thumbnail.ts +162 -2
  307. package/src/flow/CanvasNode.ts +70 -24
  308. package/src/flow/Editor.ts +440 -99
  309. package/src/flow/NodeEditor.ts +137 -9
  310. package/src/flow/Plumber.ts +89 -14
  311. package/src/flow/StickyNote.ts +14 -4
  312. package/src/flow/actions/add_contact_groups.ts +4 -1
  313. package/src/flow/actions/add_input_labels.ts +4 -1
  314. package/src/flow/actions/audio-player.ts +127 -0
  315. package/src/flow/actions/enter_flow.ts +44 -0
  316. package/src/flow/actions/play_audio.ts +64 -5
  317. package/src/flow/actions/remove_contact_groups.ts +6 -1
  318. package/src/flow/actions/say_msg.ts +94 -4
  319. package/src/flow/actions/send_broadcast.ts +6 -2
  320. package/src/flow/actions/set_contact_channel.ts +13 -1
  321. package/src/flow/actions/set_contact_status.ts +7 -5
  322. package/src/flow/actions/start_session.ts +10 -3
  323. package/src/flow/config.ts +11 -3
  324. package/src/flow/nodes/shared-rules.ts +1 -1
  325. package/src/flow/nodes/split_by_contact_field.ts +16 -5
  326. package/src/flow/nodes/split_by_expression.ts +1 -1
  327. package/src/flow/nodes/split_by_llm_categorize.ts +0 -1
  328. package/src/flow/nodes/split_by_random.ts +0 -1
  329. package/src/flow/nodes/split_by_run_result.ts +10 -4
  330. package/src/flow/nodes/terminal.ts +9 -0
  331. package/src/flow/nodes/wait_for_audio.ts +88 -0
  332. package/src/flow/nodes/wait_for_dial.ts +176 -0
  333. package/src/flow/nodes/wait_for_digits.ts +87 -2
  334. package/src/flow/nodes/wait_for_menu.ts +209 -3
  335. package/src/flow/nodes/wait_for_response.ts +1 -1
  336. package/src/flow/operators.ts +23 -5
  337. package/src/flow/types.ts +23 -1
  338. package/src/flow/utils.ts +82 -3
  339. package/src/form/ArrayEditor.ts +4 -2
  340. package/src/form/FieldRenderer.ts +71 -1
  341. package/src/interfaces.ts +2 -1
  342. package/src/layout/Dialog.ts +52 -7
  343. package/src/layout/Modax.ts +19 -2
  344. package/src/list/ContentMenu.ts +15 -1
  345. package/src/locales/es.ts +18 -13
  346. package/src/locales/fr.ts +18 -13
  347. package/src/locales/locale-codes.ts +11 -2
  348. package/src/locales/pt.ts +18 -13
  349. package/src/simulator/Simulator.ts +25 -4
  350. package/src/store/AppState.ts +120 -1
  351. package/src/store/flow-definition.d.ts +2 -0
  352. package/test/actions/add_contact_groups.test.ts +38 -0
  353. package/test/actions/add_input_labels.test.ts +67 -0
  354. package/test/actions/enter_flow.test.ts +88 -0
  355. package/test/actions/play_audio.test.ts +155 -0
  356. package/test/actions/remove_contact_groups.test.ts +29 -0
  357. package/test/actions/say_msg.test.ts +196 -0
  358. package/test/actions/send_broadcast.test.ts +44 -0
  359. package/test/actions/set_contact_channel.test.ts +88 -0
  360. package/test/actions/set_contact_field.test.ts +68 -0
  361. package/test/actions/set_contact_language.test.ts +55 -0
  362. package/test/actions/set_contact_name.test.ts +39 -0
  363. package/test/actions/set_contact_status.test.ts +64 -0
  364. package/test/actions/set_run_result.test.ts +61 -0
  365. package/test/actions/start_session.test.ts +82 -0
  366. package/test/nodes/split_by_contact_field.test.ts +59 -0
  367. package/test/nodes/split_by_run_result.test.ts +100 -0
  368. package/test/nodes/split_by_ticket.test.ts +157 -0
  369. package/test/nodes/split_by_webhook.test.ts +131 -0
  370. package/test/nodes/wait_for_audio.test.ts +182 -0
  371. package/test/nodes/wait_for_dial.test.ts +382 -0
  372. package/test/nodes/wait_for_digits.test.ts +233 -109
  373. package/test/nodes/wait_for_menu.test.ts +383 -0
  374. package/test/temba-flow-collision.test.ts +286 -6
  375. package/test/temba-flow-editor.test.ts +240 -0
  376. package/test/temba-flow-plumber.test.ts +62 -0
  377. package/test/temba-node-type-selector.test.ts +6 -6
  378. package/test/temba-select.test.ts +6 -1
  379. package/test/utils.test.ts +4 -2
  380. package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
  381. package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
  382. package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
  383. package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
  384. package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
  385. package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
@@ -2,6 +2,7 @@ import { expect } from '@open-wc/testing';
2
2
  import { wait_for_digits } from '../../src/flow/nodes/wait_for_digits';
3
3
  import { Node } from '../../src/store/flow-definition';
4
4
  import { NodeTest } from '../NodeHelper';
5
+ import { createOperatorOption } from '../../src/flow/operators';
5
6
 
6
7
  /**
7
8
  * Test suite for the wait_for_digits node configuration.
@@ -20,15 +21,29 @@ describe('wait_for_digits node config', () => {
20
21
  expect(wait_for_digits.type).to.equal('wait_for_digits');
21
22
  });
22
23
 
23
- it('has correct group', () => {
24
- expect(wait_for_digits.group).to.exist;
24
+ it('is voice-only', () => {
25
+ expect(wait_for_digits.flowTypes).to.deep.equal(['voice']);
25
26
  });
26
27
 
27
- it('is a simple node config without form or layout', () => {
28
- expect(wait_for_digits.form).to.be.undefined;
29
- expect(wait_for_digits.layout).to.be.undefined;
30
- expect(wait_for_digits.toFormData).to.be.undefined;
31
- expect(wait_for_digits.fromFormData).to.be.undefined;
28
+ it('has form with rules and result_name', () => {
29
+ expect(wait_for_digits.form).to.exist;
30
+ expect(wait_for_digits.form!.rules).to.exist;
31
+ expect(wait_for_digits.form!.result_name).to.exist;
32
+ });
33
+
34
+ it('has layout configuration', () => {
35
+ expect(wait_for_digits.layout).to.deep.equal([
36
+ {
37
+ type: 'text',
38
+ text: 'Rules match against all digits pressed by the caller followed by the # sign.'
39
+ },
40
+ 'rules',
41
+ 'result_name'
42
+ ]);
43
+ });
44
+
45
+ it('has large dialog size', () => {
46
+ expect(wait_for_digits.dialogSize).to.equal('large');
32
47
  });
33
48
  });
34
49
 
@@ -40,157 +55,266 @@ describe('wait_for_digits node config', () => {
40
55
  actions: [],
41
56
  router: {
42
57
  type: 'switch',
58
+ operand: '@input.text',
43
59
  wait: {
44
60
  type: 'msg',
45
61
  hint: {
46
- type: 'digits',
47
- count: 4
62
+ type: 'digits'
48
63
  }
49
64
  },
50
65
  result_name: 'digits',
66
+ default_category_uuid: 'all-cat',
67
+ cases: [],
51
68
  categories: [
52
69
  {
53
- uuid: 'digits-cat-1',
54
- name: 'Has Number',
55
- exit_uuid: 'digits-exit-1'
56
- },
57
- {
58
- uuid: 'digits-cat-2',
59
- name: 'Other',
60
- exit_uuid: 'digits-exit-2'
70
+ uuid: 'all-cat',
71
+ name: 'All Responses',
72
+ exit_uuid: 'all-exit'
61
73
  }
62
74
  ]
63
75
  },
64
- exits: [
65
- { uuid: 'digits-exit-1', destination_uuid: null },
66
- { uuid: 'digits-exit-2', destination_uuid: null }
67
- ]
76
+ exits: [{ uuid: 'all-exit', destination_uuid: null }]
68
77
  } as Node,
69
78
  { type: 'wait_for_digits' },
70
79
  'basic-digits-wait'
71
80
  );
72
81
  });
73
82
 
74
- it('renders single digit with timeout', async () => {
83
+ it('renders digits with rules', async () => {
75
84
  await helper.testNode(
76
85
  {
77
86
  uuid: 'test-digits-node-2',
78
87
  actions: [],
79
88
  router: {
80
89
  type: 'switch',
90
+ operand: '@input.text',
81
91
  wait: {
82
92
  type: 'msg',
83
93
  hint: {
84
- type: 'digits',
85
- count: 1
86
- },
87
- timeout: {
88
- category_uuid: 'timeout-cat',
89
- seconds: 30
94
+ type: 'digits'
90
95
  }
91
96
  },
92
- result_name: 'pin_digit',
93
- categories: [
97
+ result_name: 'pin',
98
+ default_category_uuid: 'other-cat',
99
+ cases: [
94
100
  {
95
- uuid: 'digits-cat-1',
96
- name: 'Has Number',
97
- exit_uuid: 'digits-exit-1'
98
- },
101
+ uuid: 'case-1',
102
+ type: 'has_number_eq',
103
+ arguments: ['1234'],
104
+ category_uuid: 'valid-cat'
105
+ }
106
+ ],
107
+ categories: [
99
108
  {
100
- uuid: 'timeout-cat',
101
- name: 'No Response',
102
- exit_uuid: 'timeout-exit'
109
+ uuid: 'valid-cat',
110
+ name: 'Valid PIN',
111
+ exit_uuid: 'valid-exit'
103
112
  },
104
113
  {
105
- uuid: 'digits-cat-2',
114
+ uuid: 'other-cat',
106
115
  name: 'Other',
107
- exit_uuid: 'digits-exit-2'
116
+ exit_uuid: 'other-exit'
108
117
  }
109
118
  ]
110
119
  },
111
120
  exits: [
112
- { uuid: 'digits-exit-1', destination_uuid: null },
113
- { uuid: 'timeout-exit', destination_uuid: null },
114
- { uuid: 'digits-exit-2', destination_uuid: null }
121
+ { uuid: 'valid-exit', destination_uuid: null },
122
+ { uuid: 'other-exit', destination_uuid: null }
115
123
  ]
116
124
  } as Node,
117
125
  { type: 'wait_for_digits' },
118
- 'single-digit-with-timeout'
126
+ 'digits-with-rules'
119
127
  );
120
128
  });
129
+ });
121
130
 
122
- it('renders phone number collection', async () => {
123
- await helper.testNode(
124
- {
125
- uuid: 'test-digits-node-3',
126
- actions: [],
127
- router: {
128
- type: 'switch',
129
- wait: {
130
- type: 'msg',
131
- hint: {
132
- type: 'digits',
133
- count: 10
134
- }
131
+ describe('data transformation', () => {
132
+ it('converts node to form data', () => {
133
+ const node: Node = {
134
+ uuid: 'test-node',
135
+ actions: [],
136
+ router: {
137
+ type: 'switch',
138
+ result_name: 'digits',
139
+ operand: '@input.text',
140
+ categories: [
141
+ {
142
+ uuid: 'valid-cat',
143
+ name: 'Valid',
144
+ exit_uuid: 'valid-exit'
135
145
  },
136
- result_name: 'phone_number',
137
- categories: [
138
- {
139
- uuid: 'phone-cat-1',
140
- name: 'Valid Phone',
141
- exit_uuid: 'phone-exit-1'
142
- },
143
- {
144
- uuid: 'phone-cat-2',
145
- name: 'Invalid',
146
- exit_uuid: 'phone-exit-2'
147
- },
148
- { uuid: 'phone-cat-3', name: 'Other', exit_uuid: 'phone-exit-3' }
149
- ]
150
- },
151
- exits: [
152
- { uuid: 'phone-exit-1', destination_uuid: null },
153
- { uuid: 'phone-exit-2', destination_uuid: null },
154
- { uuid: 'phone-exit-3', destination_uuid: null }
146
+ {
147
+ uuid: 'other-cat',
148
+ name: 'Other',
149
+ exit_uuid: 'other-exit'
150
+ }
151
+ ],
152
+ cases: [
153
+ {
154
+ uuid: 'case-1',
155
+ type: 'has_number_eq',
156
+ arguments: ['1234'],
157
+ category_uuid: 'valid-cat'
158
+ }
155
159
  ]
156
- } as Node,
157
- { type: 'wait_for_digits' },
158
- 'phone-number-collection'
159
- );
160
+ },
161
+ exits: [
162
+ { uuid: 'valid-exit', destination_uuid: null },
163
+ { uuid: 'other-exit', destination_uuid: null }
164
+ ]
165
+ };
166
+
167
+ const formData = wait_for_digits.toFormData!(node);
168
+
169
+ expect(formData.uuid).to.equal('test-node');
170
+ expect(formData.result_name).to.equal('digits');
171
+ expect(formData.rules).to.have.length(1);
172
+ expect(formData.rules[0]).to.deep.equal({
173
+ operator: createOperatorOption('has_number_eq'),
174
+ value1: '1234',
175
+ value2: '',
176
+ category: 'Valid'
177
+ });
160
178
  });
161
179
 
162
- it('renders verification code', async () => {
163
- await helper.testNode(
164
- {
165
- uuid: 'test-digits-node-4',
166
- actions: [],
167
- router: {
168
- type: 'switch',
169
- wait: {
170
- type: 'msg',
171
- hint: {
172
- type: 'digits',
173
- count: 6
174
- }
180
+ it('converts node with no rules to form data', () => {
181
+ const node: Node = {
182
+ uuid: 'test-node',
183
+ actions: [],
184
+ router: {
185
+ type: 'switch',
186
+ result_name: 'digits',
187
+ categories: []
188
+ },
189
+ exits: []
190
+ };
191
+
192
+ const formData = wait_for_digits.toFormData!(node);
193
+ expect(formData.rules).to.deep.equal([]);
194
+ expect(formData.result_name).to.equal('digits');
195
+ });
196
+
197
+ it('converts form data to node with rules', () => {
198
+ const formData = {
199
+ uuid: 'test-node',
200
+ result_name: 'digits',
201
+ rules: [
202
+ {
203
+ operator: { value: 'has_number_eq', name: 'is equal to' },
204
+ value1: '1234',
205
+ value2: '',
206
+ category: 'Valid PIN'
207
+ }
208
+ ]
209
+ };
210
+
211
+ const originalNode: Node = {
212
+ uuid: 'test-node',
213
+ actions: [],
214
+ exits: [],
215
+ router: { type: 'switch', categories: [] }
216
+ };
217
+
218
+ const result = wait_for_digits.fromFormData!(formData, originalNode);
219
+
220
+ expect(result.uuid).to.equal('test-node');
221
+ expect(result.router?.result_name).to.equal('digits');
222
+ expect(result.router?.operand).to.equal('@input.text');
223
+ expect(result.router?.categories).to.have.length(2); // Valid PIN, Other
224
+ expect(result.router?.cases).to.have.length(1);
225
+ expect(result.exits).to.have.length(2);
226
+
227
+ // Check wait config has digits hint (without count)
228
+ expect(result.router?.wait?.type).to.equal('msg');
229
+ expect(result.router?.wait?.hint?.type).to.equal('digits');
230
+ expect(result.router?.wait?.hint?.count).to.be.undefined;
231
+ });
232
+
233
+ it('converts form data with no rules', () => {
234
+ const formData = {
235
+ uuid: 'test-node',
236
+ result_name: 'digits',
237
+ rules: []
238
+ };
239
+
240
+ const originalNode: Node = {
241
+ uuid: 'test-node',
242
+ actions: [],
243
+ exits: [],
244
+ router: { type: 'switch', categories: [] }
245
+ };
246
+
247
+ const result = wait_for_digits.fromFormData!(formData, originalNode);
248
+
249
+ // Should have just "All Responses" default category (no user rules)
250
+ expect(result.router?.categories).to.have.length(1);
251
+ expect(result.router!.categories[0].name).to.equal('All Responses');
252
+ expect(result.router?.cases).to.have.length(0);
253
+ expect(result.router?.wait?.hint?.type).to.equal('digits');
254
+ });
255
+
256
+ it('preserves category UUIDs', () => {
257
+ const formData = {
258
+ uuid: 'test-node',
259
+ result_name: 'digits',
260
+ rules: [
261
+ {
262
+ operator: { value: 'has_number_eq', name: 'is equal to' },
263
+ value1: '1234',
264
+ value2: '',
265
+ category: 'Valid'
266
+ }
267
+ ]
268
+ };
269
+
270
+ const originalNode: Node = {
271
+ uuid: 'test-node',
272
+ actions: [],
273
+ router: {
274
+ type: 'switch',
275
+ categories: [
276
+ {
277
+ uuid: 'orig-valid',
278
+ name: 'Valid',
279
+ exit_uuid: 'orig-valid-exit'
175
280
  },
176
- result_name: 'verification_code',
177
- categories: [
178
- {
179
- uuid: 'code-cat-1',
180
- name: 'Valid Code',
181
- exit_uuid: 'code-exit-1'
182
- },
183
- { uuid: 'code-cat-2', name: 'Other', exit_uuid: 'code-exit-2' }
184
- ]
185
- },
186
- exits: [
187
- { uuid: 'code-exit-1', destination_uuid: null },
188
- { uuid: 'code-exit-2', destination_uuid: null }
281
+ {
282
+ uuid: 'orig-other',
283
+ name: 'Other',
284
+ exit_uuid: 'orig-other-exit'
285
+ }
286
+ ],
287
+ cases: [
288
+ {
289
+ uuid: 'orig-case',
290
+ type: 'has_number_eq',
291
+ arguments: ['1234'],
292
+ category_uuid: 'orig-valid'
293
+ }
189
294
  ]
190
- } as Node,
191
- { type: 'wait_for_digits' },
192
- 'verification-code'
193
- );
295
+ },
296
+ exits: [
297
+ { uuid: 'orig-valid-exit', destination_uuid: 'dest-1' },
298
+ { uuid: 'orig-other-exit', destination_uuid: null }
299
+ ]
300
+ };
301
+
302
+ const result = wait_for_digits.fromFormData!(formData, originalNode);
303
+
304
+ const valid = result.router!.categories.find((c) => c.name === 'Valid');
305
+ expect(valid?.uuid).to.equal('orig-valid');
306
+ expect(valid?.exit_uuid).to.equal('orig-valid-exit');
307
+
308
+ const other = result.router!.categories.find((c) => c.name === 'Other');
309
+ expect(other?.uuid).to.equal('orig-other');
310
+ });
311
+ });
312
+
313
+ describe('validation', () => {
314
+ it('validates with no errors', () => {
315
+ const formData = { rules: [] };
316
+ const result = wait_for_digits.validate!(formData);
317
+ expect(result.valid).to.be.true;
194
318
  });
195
319
  });
196
320
  });