@nyaruka/temba-components 0.129.10 → 0.130.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 (316) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/demo/components/flow/example.html +5 -1
  3. package/demo/data/flows/sample-flow.json +217 -113
  4. package/dist/temba-components.js +310 -356
  5. package/dist/temba-components.js.map +1 -1
  6. package/out-tsc/src/events.js.map +1 -1
  7. package/out-tsc/src/flow/CanvasNode.js +3 -35
  8. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  9. package/out-tsc/src/flow/Editor.js +9 -6
  10. package/out-tsc/src/flow/Editor.js.map +1 -1
  11. package/out-tsc/src/flow/NodeEditor.js +44 -11
  12. package/out-tsc/src/flow/NodeEditor.js.map +1 -1
  13. package/out-tsc/src/flow/actions/add_contact_groups.js +1 -1
  14. package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
  15. package/out-tsc/src/flow/actions/add_contact_urn.js +1 -1
  16. package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
  17. package/out-tsc/src/flow/actions/add_input_labels.js +1 -1
  18. package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
  19. package/out-tsc/src/flow/actions/remove_contact_groups.js +1 -1
  20. package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -1
  21. package/out-tsc/src/flow/actions/send_email.js +9 -0
  22. package/out-tsc/src/flow/actions/send_email.js.map +1 -1
  23. package/out-tsc/src/flow/actions/send_msg.js +7 -8
  24. package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
  25. package/out-tsc/src/flow/actions/set_contact_channel.js +25 -4
  26. package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
  27. package/out-tsc/src/flow/actions/set_contact_field.js +51 -1
  28. package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
  29. package/out-tsc/src/flow/actions/set_contact_language.js +70 -2
  30. package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
  31. package/out-tsc/src/flow/actions/set_contact_name.js +27 -2
  32. package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
  33. package/out-tsc/src/flow/actions/set_contact_status.js +32 -2
  34. package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
  35. package/out-tsc/src/flow/actions/set_run_result.js +13 -11
  36. package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
  37. package/out-tsc/src/flow/actions/split_by_expression_example.js +4 -4
  38. package/out-tsc/src/flow/actions/split_by_expression_example.js.map +1 -1
  39. package/out-tsc/src/flow/config.js +2 -8
  40. package/out-tsc/src/flow/config.js.map +1 -1
  41. package/out-tsc/src/flow/forms/index.js +2 -0
  42. package/out-tsc/src/flow/forms/index.js.map +1 -0
  43. package/out-tsc/src/flow/nodes/split_by_llm.js +101 -0
  44. package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -0
  45. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +4 -89
  46. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
  47. package/out-tsc/src/flow/nodes/split_by_random.js +117 -0
  48. package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
  49. package/out-tsc/src/flow/nodes/split_by_subflow.js +123 -3
  50. package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
  51. package/out-tsc/src/flow/nodes/split_by_ticket.js +114 -13
  52. package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -1
  53. package/out-tsc/src/flow/nodes/split_by_webhook.js +158 -12
  54. package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
  55. package/out-tsc/src/flow/types.js.map +1 -1
  56. package/out-tsc/src/form/ArrayEditor.js +9 -25
  57. package/out-tsc/src/form/ArrayEditor.js.map +1 -1
  58. package/out-tsc/src/form/FieldRenderer.js +6 -64
  59. package/out-tsc/src/form/FieldRenderer.js.map +1 -1
  60. package/out-tsc/src/form/select/Select.js +29 -58
  61. package/out-tsc/src/form/select/Select.js.map +1 -1
  62. package/out-tsc/src/live/ContactChat.js +48 -66
  63. package/out-tsc/src/live/ContactChat.js.map +1 -1
  64. package/out-tsc/src/utils.js +118 -0
  65. package/out-tsc/src/utils.js.map +1 -1
  66. package/out-tsc/test/nodes/split_by_llm.test.js +174 -0
  67. package/out-tsc/test/nodes/split_by_llm.test.js.map +1 -0
  68. package/out-tsc/test/nodes/split_by_random.test.js +0 -6
  69. package/out-tsc/test/nodes/split_by_random.test.js.map +1 -1
  70. package/out-tsc/test/temba-field-renderer.test.js +6 -3
  71. package/out-tsc/test/temba-field-renderer.test.js.map +1 -1
  72. package/out-tsc/test/utils.test.js +18 -0
  73. package/out-tsc/test/utils.test.js.map +1 -1
  74. package/package.json +1 -1
  75. package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
  76. package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
  77. package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
  78. package/screenshots/truth/actions/add_contact_groups/editor/multiple-groups.png +0 -0
  79. package/screenshots/truth/actions/add_contact_groups/editor/single-group.png +0 -0
  80. package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
  81. package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
  82. package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
  83. package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
  84. package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
  85. package/screenshots/truth/actions/call_llm/editor/information-extraction.png +0 -0
  86. package/screenshots/truth/actions/call_llm/editor/sentiment-analysis.png +0 -0
  87. package/screenshots/truth/actions/call_llm/editor/summarization.png +0 -0
  88. package/screenshots/truth/actions/call_llm/editor/translation-task.png +0 -0
  89. package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
  90. package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
  91. package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
  92. package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
  93. package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
  94. package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
  95. package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
  96. package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
  97. package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
  98. package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
  99. package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
  100. package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
  101. package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
  102. package/screenshots/truth/actions/send_email/editor/empty-body.png +0 -0
  103. package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
  104. package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
  105. package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
  106. package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
  107. package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
  108. package/screenshots/truth/actions/send_email/editor/with-expressions.png +0 -0
  109. package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
  110. package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
  111. package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
  112. package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
  113. package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
  114. package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
  115. package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
  116. package/screenshots/truth/checkbox/checkbox-label-background-hover.png +0 -0
  117. package/screenshots/truth/checkbox/checkbox-no-label-no-background-hover.png +0 -0
  118. package/screenshots/truth/checkbox/checkbox-whitespace-label-no-background-hover.png +0 -0
  119. package/screenshots/truth/checkbox/checkbox-with-help-text.png +0 -0
  120. package/screenshots/truth/checkbox/checked.png +0 -0
  121. package/screenshots/truth/checkbox/default.png +0 -0
  122. package/screenshots/truth/colorpicker/default.png +0 -0
  123. package/screenshots/truth/colorpicker/focused.png +0 -0
  124. package/screenshots/truth/colorpicker/initialized.png +0 -0
  125. package/screenshots/truth/colorpicker/selected.png +0 -0
  126. package/screenshots/truth/compose/attachments-tab.png +0 -0
  127. package/screenshots/truth/compose/attachments-with-files.png +0 -0
  128. package/screenshots/truth/compose/intial-text.png +0 -0
  129. package/screenshots/truth/compose/no-counter.png +0 -0
  130. package/screenshots/truth/compose/wraps-text-and-spaces.png +0 -0
  131. package/screenshots/truth/compose/wraps-text-and-url.png +0 -0
  132. package/screenshots/truth/compose/wraps-text-no-spaces.png +0 -0
  133. package/screenshots/truth/contacts/chat-failure.png +0 -0
  134. package/screenshots/truth/contacts/chat-for-active-contact.png +0 -0
  135. package/screenshots/truth/contacts/chat-sends-attachments-only.png +0 -0
  136. package/screenshots/truth/contacts/chat-sends-text-and-attachments.png +0 -0
  137. package/screenshots/truth/contacts/chat-sends-text-only.png +0 -0
  138. package/screenshots/truth/counter/summary.png +0 -0
  139. package/screenshots/truth/counter/text.png +0 -0
  140. package/screenshots/truth/counter/unicode-variables.png +0 -0
  141. package/screenshots/truth/counter/unicode.png +0 -0
  142. package/screenshots/truth/counter/variable.png +0 -0
  143. package/screenshots/truth/datepicker/date-truncated-time.png +0 -0
  144. package/screenshots/truth/datepicker/date.png +0 -0
  145. package/screenshots/truth/datepicker/initial-timezone.png +0 -0
  146. package/screenshots/truth/datepicker/range-picker-editing-start.png +0 -0
  147. package/screenshots/truth/datepicker/updated-keyboard-date.png +0 -0
  148. package/screenshots/truth/dialog/focused.png +0 -0
  149. package/screenshots/truth/dropdown/right-edge-collision.png +0 -0
  150. package/screenshots/truth/editor/router.png +0 -0
  151. package/screenshots/truth/editor/send_msg.png +0 -0
  152. package/screenshots/truth/editor/set_contact_language.png +0 -0
  153. package/screenshots/truth/editor/set_contact_name.png +0 -0
  154. package/screenshots/truth/editor/set_run_result.png +0 -0
  155. package/screenshots/truth/editor/wait.png +0 -0
  156. package/screenshots/truth/field-renderer/checkbox-checked.png +0 -0
  157. package/screenshots/truth/field-renderer/checkbox-unchecked.png +0 -0
  158. package/screenshots/truth/field-renderer/checkbox-with-errors.png +0 -0
  159. package/screenshots/truth/field-renderer/context-comparison.png +0 -0
  160. package/screenshots/truth/field-renderer/key-value-with-label.png +0 -0
  161. package/screenshots/truth/field-renderer/message-editor-with-label.png +0 -0
  162. package/screenshots/truth/field-renderer/select-multi.png +0 -0
  163. package/screenshots/truth/field-renderer/select-no-label.png +0 -0
  164. package/screenshots/truth/field-renderer/select-with-label.png +0 -0
  165. package/screenshots/truth/field-renderer/text-evaluated.png +0 -0
  166. package/screenshots/truth/field-renderer/text-no-label.png +0 -0
  167. package/screenshots/truth/field-renderer/text-with-errors.png +0 -0
  168. package/screenshots/truth/field-renderer/text-with-label.png +0 -0
  169. package/screenshots/truth/field-renderer/textarea-evaluated.png +0 -0
  170. package/screenshots/truth/field-renderer/textarea-with-label.png +0 -0
  171. package/screenshots/truth/integration/checkbox-markdown-errors.png +0 -0
  172. package/screenshots/truth/list/fields-dragging.png +0 -0
  173. package/screenshots/truth/list/fields-filtered.png +0 -0
  174. package/screenshots/truth/list/fields-hovered.png +0 -0
  175. package/screenshots/truth/list/fields.png +0 -0
  176. package/screenshots/truth/list/items-selected.png +0 -0
  177. package/screenshots/truth/list/items-updated.png +0 -0
  178. package/screenshots/truth/list/items.png +0 -0
  179. package/screenshots/truth/menu/menu-focused-with items.png +0 -0
  180. package/screenshots/truth/menu/menu-refresh-1.png +0 -0
  181. package/screenshots/truth/menu/menu-refresh-2.png +0 -0
  182. package/screenshots/truth/menu/menu-submenu.png +0 -0
  183. package/screenshots/truth/menu/menu-tasks-nextup.png +0 -0
  184. package/screenshots/truth/menu/menu-tasks.png +0 -0
  185. package/screenshots/truth/message-editor/autogrow-initial-content.png +0 -0
  186. package/screenshots/truth/message-editor/default.png +0 -0
  187. package/screenshots/truth/message-editor/drag-highlight.png +0 -0
  188. package/screenshots/truth/message-editor/filtered-attachments.png +0 -0
  189. package/screenshots/truth/message-editor/with-completion.png +0 -0
  190. package/screenshots/truth/message-editor/with-properties.png +0 -0
  191. package/screenshots/truth/modax/form.png +0 -0
  192. package/screenshots/truth/modax/simple.png +0 -0
  193. package/screenshots/truth/nodes/split_by_llm/editor/information-extraction.png +0 -0
  194. package/screenshots/truth/nodes/split_by_llm/editor/sentiment-analysis.png +0 -0
  195. package/screenshots/truth/nodes/split_by_llm/editor/summarization.png +0 -0
  196. package/screenshots/truth/nodes/split_by_llm/editor/translation-task.png +0 -0
  197. package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
  198. package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
  199. package/screenshots/truth/nodes/split_by_llm/render/summarization.png +0 -0
  200. package/screenshots/truth/nodes/split_by_llm/render/translation-task.png +0 -0
  201. package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
  202. package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
  203. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  204. package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
  205. package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
  206. package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
  207. package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
  208. package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
  209. package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
  210. package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
  211. package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
  212. package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
  213. package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
  214. package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
  215. package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
  216. package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
  217. package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
  218. package/screenshots/truth/omnibox/selected.png +0 -0
  219. package/screenshots/truth/options/block.png +0 -0
  220. package/screenshots/truth/run-list/basic.png +0 -0
  221. package/screenshots/truth/select/disabled-multi-selection.png +0 -0
  222. package/screenshots/truth/select/disabled-selection.png +0 -0
  223. package/screenshots/truth/select/disabled.png +0 -0
  224. package/screenshots/truth/select/embedded.png +0 -0
  225. package/screenshots/truth/select/empty-options.png +0 -0
  226. package/screenshots/truth/select/expression-selected.png +0 -0
  227. package/screenshots/truth/select/expressions.png +0 -0
  228. package/screenshots/truth/select/functions.png +0 -0
  229. package/screenshots/truth/select/local-options.png +0 -0
  230. package/screenshots/truth/select/multi-with-endpoint.png +0 -0
  231. package/screenshots/truth/select/multiple-initial-values.png +0 -0
  232. package/screenshots/truth/select/remote-options.png +0 -0
  233. package/screenshots/truth/select/search-enabled.png +0 -0
  234. package/screenshots/truth/select/search-multi-no-matches.png +0 -0
  235. package/screenshots/truth/select/search-selected-focus.png +0 -0
  236. package/screenshots/truth/select/search-selected.png +0 -0
  237. package/screenshots/truth/select/search-with-selected.png +0 -0
  238. package/screenshots/truth/select/searching.png +0 -0
  239. package/screenshots/truth/select/selected-multi-maxitems-reached.png +0 -0
  240. package/screenshots/truth/select/selected-multi.png +0 -0
  241. package/screenshots/truth/select/selected-single.png +0 -0
  242. package/screenshots/truth/select/selection-clearable.png +0 -0
  243. package/screenshots/truth/select/static-initial-value.png +0 -0
  244. package/screenshots/truth/select/static-initial-via-selected.png +0 -0
  245. package/screenshots/truth/select/truncated-selection.png +0 -0
  246. package/screenshots/truth/select/with-placeholder.png +0 -0
  247. package/screenshots/truth/select/without-placeholder.png +0 -0
  248. package/screenshots/truth/slider/update-slider-on-circle-dragged.png +0 -0
  249. package/screenshots/truth/templates/default.png +0 -0
  250. package/screenshots/truth/templates/unapproved.png +0 -0
  251. package/screenshots/truth/textinput/autogrow-initial.png +0 -0
  252. package/screenshots/truth/textinput/input-disabled.png +0 -0
  253. package/screenshots/truth/textinput/input-focused.png +0 -0
  254. package/screenshots/truth/textinput/input-form.png +0 -0
  255. package/screenshots/truth/textinput/input-inserted.png +0 -0
  256. package/screenshots/truth/textinput/input-placeholder.png +0 -0
  257. package/screenshots/truth/textinput/input-updated.png +0 -0
  258. package/screenshots/truth/textinput/input.png +0 -0
  259. package/screenshots/truth/textinput/textarea-focused.png +0 -0
  260. package/screenshots/truth/textinput/textarea.png +0 -0
  261. package/src/events.ts +4 -2
  262. package/src/flow/CanvasNode.ts +2 -39
  263. package/src/flow/Editor.ts +6 -3
  264. package/src/flow/NodeEditor.ts +54 -13
  265. package/src/flow/actions/add_contact_groups.ts +1 -1
  266. package/src/flow/actions/add_contact_urn.ts +1 -1
  267. package/src/flow/actions/add_input_labels.ts +1 -1
  268. package/src/flow/actions/remove_contact_groups.ts +1 -1
  269. package/src/flow/actions/send_email.ts +11 -1
  270. package/src/flow/actions/send_msg.ts +20 -11
  271. package/src/flow/actions/set_contact_channel.ts +28 -5
  272. package/src/flow/actions/set_contact_field.ts +56 -2
  273. package/src/flow/actions/set_contact_language.ts +74 -3
  274. package/src/flow/actions/set_contact_name.ts +31 -3
  275. package/src/flow/actions/set_contact_status.ts +36 -3
  276. package/src/flow/actions/set_run_result.ts +13 -15
  277. package/src/flow/actions/split_by_expression_example.ts +4 -4
  278. package/src/flow/config.ts +2 -8
  279. package/src/flow/forms/index.ts +1 -0
  280. package/src/flow/nodes/split_by_llm.ts +119 -0
  281. package/src/flow/nodes/split_by_llm_categorize.ts +13 -116
  282. package/src/flow/nodes/split_by_random.ts +148 -0
  283. package/src/flow/nodes/split_by_subflow.ts +153 -3
  284. package/src/flow/nodes/split_by_ticket.ts +134 -12
  285. package/src/flow/nodes/split_by_webhook.ts +185 -12
  286. package/src/flow/types.ts +2 -1
  287. package/src/form/ArrayEditor.ts +6 -20
  288. package/src/form/FieldRenderer.ts +6 -65
  289. package/src/form/select/Select.ts +34 -66
  290. package/src/live/ContactChat.ts +56 -58
  291. package/src/store/flow-definition.d.ts +8 -2
  292. package/src/utils.ts +196 -0
  293. package/static/api/fields.json +93 -1208
  294. package/static/api/workspace.json +23 -0
  295. package/test/nodes/split_by_llm.test.ts +232 -0
  296. package/test/nodes/split_by_random.test.ts +0 -7
  297. package/test/temba-field-renderer.test.ts +26 -13
  298. package/test/utils.test.ts +20 -0
  299. package/test-assets/style.css +36 -234
  300. package/web-dev-server.config.mjs +28 -0
  301. package/web-test-runner.config.mjs +38 -1
  302. package/out-tsc/src/flow/actions/call_llm.js +0 -64
  303. package/out-tsc/src/flow/actions/call_llm.js.map +0 -1
  304. package/out-tsc/src/flow/actions/call_webhook.js +0 -131
  305. package/out-tsc/src/flow/actions/call_webhook.js.map +0 -1
  306. package/out-tsc/src/flow/actions/enter_flow.js +0 -14
  307. package/out-tsc/src/flow/actions/enter_flow.js.map +0 -1
  308. package/out-tsc/src/flow/actions/open_ticket.js +0 -73
  309. package/out-tsc/src/flow/actions/open_ticket.js.map +0 -1
  310. package/out-tsc/test/actions/call_llm.test.js +0 -103
  311. package/out-tsc/test/actions/call_llm.test.js.map +0 -1
  312. package/src/flow/actions/call_llm.ts +0 -66
  313. package/src/flow/actions/call_webhook.ts +0 -143
  314. package/src/flow/actions/enter_flow.ts +0 -15
  315. package/src/flow/actions/open_ticket.ts +0 -83
  316. package/test/actions/call_llm.test.ts +0 -137
Binary file
package/src/events.ts CHANGED
@@ -30,8 +30,10 @@ export interface ChannelEvent extends ContactEvent {
30
30
 
31
31
  export interface ContactLanguageChangedEvent extends ContactEvent {
32
32
  language: string;
33
- step_uuid: string;
34
- session_uuid: string;
33
+ }
34
+
35
+ export interface ContactStatusChangedEvent extends ContactEvent {
36
+ status: string;
35
37
  }
36
38
 
37
39
  export interface OptInEvent extends ContactEvent {
@@ -873,30 +873,7 @@ export class CanvasNode extends RapidElement {
873
873
  private renderRouter(router: Router, ui: NodeUI) {
874
874
  const nodeConfig = NODE_CONFIG[ui.type];
875
875
  if (nodeConfig) {
876
- // For tests that call renderRouter directly without setting this.node
877
- const hasActions = this.node ? this.node.actions.length > 0 : false;
878
- const isRemoving =
879
- this.node &&
880
- this.node.actions.length === 0 &&
881
- this.actionRemovingState.has(this.node.uuid);
882
-
883
876
  return html`<div class="router" style="position: relative;">
884
- ${!hasActions
885
- ? html` <button
886
- class="remove-button"
887
- @click=${(e: MouseEvent) => this.handleNodeRemoveClick(e)}
888
- title="Remove node"
889
- >
890
-
891
- </button>
892
- <div
893
- @mousedown=${(e: MouseEvent) => this.handleNodeMouseDown(e)}
894
- @mouseup=${(e: MouseEvent) => this.handleNodeMouseUp(e)}
895
- style="cursor: pointer;"
896
- >
897
- ${this.renderNodeTitle(nodeConfig, isRemoving)}
898
- </div>`
899
- : ''}
900
877
  ${router.result_name
901
878
  ? html`<div
902
879
  class="body"
@@ -969,7 +946,7 @@ export class CanvasNode extends RapidElement {
969
946
  : ''}"
970
947
  style="left:${this.ui.position.left}px;top:${this.ui.position.top}px"
971
948
  >
972
- ${nodeConfig && nodeConfig.render
949
+ ${nodeConfig && nodeConfig.type !== 'execute_actions'
973
950
  ? html`<div class="router" style="position: relative;">
974
951
  <button
975
952
  class="remove-button"
@@ -987,7 +964,7 @@ export class CanvasNode extends RapidElement {
987
964
  nodeConfig,
988
965
  this.actionRemovingState.has(this.node.uuid)
989
966
  )}
990
- ${nodeConfig.render(this.node)}
967
+ ${nodeConfig.render ? nodeConfig.render(this.node) : null}
991
968
  </div>
992
969
  </div>`
993
970
  : this.node.actions.length > 0
@@ -1007,20 +984,6 @@ export class CanvasNode extends RapidElement {
1007
984
  (action) => action.uuid,
1008
985
  (action, index) => this.renderAction(this.node, action, index)
1009
986
  )}`
1010
- : !this.node.router && nodeConfig && nodeConfig.name
1011
- ? html`<div class="router" style="position: relative;">
1012
- <button
1013
- class="remove-button"
1014
- @click=${(e: MouseEvent) => this.handleNodeRemoveClick(e)}
1015
- title="Remove node"
1016
- >
1017
-
1018
- </button>
1019
- ${this.renderNodeTitle(
1020
- nodeConfig,
1021
- this.actionRemovingState.has(this.node.uuid)
1022
- )}
1023
- </div>`
1024
987
  : ''}
1025
988
  ${this.node.router
1026
989
  ? html` ${this.renderRouter(this.node.router, this.ui)}
@@ -653,7 +653,7 @@ export class Editor extends RapidElement {
653
653
  this.definition?.nodes.forEach((node) => {
654
654
  const nodeElement = this.querySelector(`[id="${node.uuid}"]`);
655
655
  if (nodeElement) {
656
- const position = this.definition._ui.nodes[node.uuid]?.position;
656
+ const position = this.definition._ui?.nodes[node.uuid]?.position;
657
657
  if (position) {
658
658
  const rect = nodeElement.getBoundingClientRect();
659
659
  const canvasRect =
@@ -1085,8 +1085,11 @@ export class Editor extends RapidElement {
1085
1085
  this.definition.nodes,
1086
1086
  (node) => node.uuid,
1087
1087
  (node) => {
1088
- const position =
1089
- this.definition._ui.nodes[node.uuid].position;
1088
+ const position = this.definition._ui?.nodes[node.uuid]
1089
+ ?.position || {
1090
+ left: 0,
1091
+ top: 0
1092
+ };
1090
1093
 
1091
1094
  const dragging =
1092
1095
  this.isDragging &&
@@ -320,14 +320,6 @@ export class NodeEditor extends RapidElement {
320
320
  this.isOpen = true;
321
321
  }
322
322
 
323
- private closeDialog(): void {
324
- this.isOpen = false;
325
- this.formData = {};
326
- this.errors = {};
327
- this.groupCollapseState = {};
328
- this.groupHoverState = {};
329
- }
330
-
331
323
  private initializeFormData(): void {
332
324
  const nodeConfig = this.getNodeConfig();
333
325
 
@@ -393,9 +385,51 @@ export class NodeEditor extends RapidElement {
393
385
  }
394
386
  });
395
387
 
388
+ // Convert select fields to array format
389
+ const config = this.getConfig();
390
+ if (config?.form) {
391
+ this.processSelectFields(processed, config.form);
392
+ }
393
+
396
394
  this.formData = processed;
397
395
  }
398
396
 
397
+ private processSelectFields(data: any, formConfig: any): void {
398
+ Object.entries(formConfig).forEach(
399
+ ([fieldName, fieldConfig]: [string, any]) => {
400
+ const value = data[fieldName];
401
+
402
+ // Handle top-level select fields
403
+ if (fieldConfig.type === 'select' && value) {
404
+ data[fieldName] = this.convertToSelectArray(value);
405
+ }
406
+
407
+ // Handle select fields within array items
408
+ if (
409
+ fieldConfig.type === 'array' &&
410
+ Array.isArray(value) &&
411
+ fieldConfig.itemConfig
412
+ ) {
413
+ value.forEach((item: any) => {
414
+ this.processSelectFields(item, fieldConfig.itemConfig);
415
+ });
416
+ }
417
+ }
418
+ );
419
+ }
420
+
421
+ private convertToSelectArray(value: any): any[] {
422
+ if (Array.isArray(value)) {
423
+ return value.map((v) =>
424
+ typeof v === 'string' ? { name: v, value: v } : v
425
+ );
426
+ } else if (typeof value === 'string') {
427
+ return [{ name: value, value: value }];
428
+ } else {
429
+ return [value];
430
+ }
431
+ }
432
+
399
433
  private isKeyValueField(fieldName: string): boolean {
400
434
  // Check if this field is configured as a key-value type
401
435
  const config = this.getConfig();
@@ -533,6 +567,18 @@ export class NodeEditor extends RapidElement {
533
567
  if (config.form) {
534
568
  Object.entries(config.form).forEach(([fieldName, fieldConfig]) => {
535
569
  const value = this.formData[fieldName];
570
+ if (fieldConfig.type === 'select' && fieldConfig.allowCreate) {
571
+ // check our values to see if any have arbitrary set
572
+ let selected = this.formData[fieldName];
573
+ selected = Array.isArray(selected)
574
+ ? selected.find((v: any) => v.arbitrary)
575
+ : null;
576
+
577
+ if (selected && selected.arbitrary) {
578
+ errors[fieldName] =
579
+ 'There was an error creating' + ' "' + selected.name + '"';
580
+ }
581
+ }
536
582
 
537
583
  // Check required fields
538
584
  if (
@@ -856,11 +902,6 @@ export class NodeEditor extends RapidElement {
856
902
  // Handle different component types like ActionEditor does
857
903
  if (target.tagName === 'TEMBA-CHECKBOX') {
858
904
  value = target.checked;
859
- } else if (
860
- target.tagName === 'TEMBA-SELECT' &&
861
- (target.multi || target.emails || target.tags)
862
- ) {
863
- value = target.values || [];
864
905
  } else if (target.values !== undefined) {
865
906
  value = target.values;
866
907
  } else {
@@ -5,7 +5,7 @@ import { renderNamedObjects } from '../utils';
5
5
 
6
6
  export const add_contact_groups: ActionConfig = {
7
7
  name: 'Add to Group',
8
- color: COLORS.add,
8
+ color: COLORS.update,
9
9
  render: (_node: Node, action: AddToGroup) => {
10
10
  return html`<div>${renderNamedObjects(action.groups, 'group')}</div>`;
11
11
  },
@@ -4,7 +4,7 @@ import { Node, AddContactUrn } from '../../store/flow-definition';
4
4
  import { urnSchemeMap } from '../utils';
5
5
 
6
6
  export const add_contact_urn: ActionConfig = {
7
- name: 'Add Contact URN',
7
+ name: 'Add URN',
8
8
  color: COLORS.update,
9
9
  render: (_node: Node, action: AddContactUrn) => {
10
10
  const friendlyScheme = urnSchemeMap[action.scheme] || action.scheme;
@@ -5,7 +5,7 @@ import { renderNamedObjects } from '../utils';
5
5
 
6
6
  export const add_input_labels: ActionConfig = {
7
7
  name: 'Add Input Labels',
8
- color: COLORS.update,
8
+ color: COLORS.save,
9
9
  render: (_node: Node, action: AddInputLabels) => {
10
10
  return html`<div>${renderNamedObjects(action.labels, 'label')}</div>`;
11
11
  },
@@ -5,7 +5,7 @@ import { renderNamedObjects } from '../utils';
5
5
 
6
6
  export const remove_contact_groups: ActionConfig = {
7
7
  name: 'Remove from Group',
8
- color: COLORS.remove,
8
+ color: COLORS.update,
9
9
  render: (_node: Node, action: RemoveFromGroup) => {
10
10
  if (action.all_groups) {
11
11
  return html`<div>Remove from all groups</div>`;
@@ -19,7 +19,6 @@ export const send_email: ActionConfig = {
19
19
  </div>
20
20
  </div>`;
21
21
  },
22
-
23
22
  form: {
24
23
  addresses: {
25
24
  type: 'select',
@@ -44,6 +43,17 @@ export const send_email: ActionConfig = {
44
43
  minHeight: 175
45
44
  }
46
45
  },
46
+ fromFormData: (formData: any): SendEmail => {
47
+ return {
48
+ uuid: formData.uuid,
49
+ type: 'send_email',
50
+ addresses: formData.addresses.map(
51
+ (addr: { name: string; value: string }) => addr.value
52
+ ),
53
+ subject: formData.subject,
54
+ body: formData.body
55
+ };
56
+ },
47
57
  validate: (action: SendEmail): ValidationResult => {
48
58
  const errors: { [key: string]: string } = {};
49
59
 
@@ -2,6 +2,7 @@ import { html } from 'lit-html';
2
2
  import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
3
3
  import { ActionConfig, COLORS, ValidationResult } from '../types';
4
4
  import { Node, SendMsg } from '../../store/flow-definition';
5
+ import { titleCase } from '../../utils';
5
6
 
6
7
  export const send_msg: ActionConfig = {
7
8
  name: 'Send Message',
@@ -65,10 +66,10 @@ export const send_msg: ActionConfig = {
65
66
  type: {
66
67
  type: 'select',
67
68
  options: [
68
- { value: 'image', label: 'Image' },
69
- { value: 'audio', label: 'Audio' },
70
- { value: 'video', label: 'Video' },
71
- { value: 'document', label: 'Document' }
69
+ { value: 'image', name: 'Image' },
70
+ { value: 'audio', name: 'Audio' },
71
+ { value: 'video', name: 'Video' },
72
+ { value: 'document', name: 'Document' }
72
73
  ],
73
74
  required: true,
74
75
  searchable: false
@@ -116,7 +117,10 @@ export const send_msg: ActionConfig = {
116
117
  ],
117
118
  toFormData: (action: SendMsg) => {
118
119
  // Extract runtime attachments from the text field attachments
119
- const runtimeAttachments: { type: string; expression: string }[] = [];
120
+ const runtimeAttachments: {
121
+ type: { name: string; value: string };
122
+ expression: string;
123
+ }[] = [];
120
124
  const staticAttachments: string[] = [];
121
125
 
122
126
  if (action.attachments && Array.isArray(action.attachments)) {
@@ -127,13 +131,11 @@ export const send_msg: ActionConfig = {
127
131
  const value = attachment.substring(colonIndex + 1);
128
132
 
129
133
  if (!contentType.includes('/')) {
130
- // This is a runtime attachment
131
134
  runtimeAttachments.push({
132
- type: contentType,
135
+ type: { name: titleCase(contentType), value: contentType },
133
136
  expression: value
134
137
  });
135
138
  } else {
136
- // This is a static attachment
137
139
  staticAttachments.push(attachment);
138
140
  }
139
141
  }
@@ -165,10 +167,17 @@ export const send_msg: ActionConfig = {
165
167
  // Combine static attachments from text field with runtime attachments
166
168
  const staticAttachments = data.attachments || [];
167
169
  const runtimeAttachments = (data.runtime_attachments || [])
168
- .filter((item: any) => item && item.type && item.expression) // Filter out invalid items
170
+ .filter(
171
+ (item: {
172
+ type: [{ name: string; value: string }];
173
+ expression: string;
174
+ }) => item && item.type && item.expression
175
+ ) // Filter out invalid items
169
176
  .map(
170
- (item: { type: string; expression: string }) =>
171
- `${item.type}:${item.expression}`
177
+ (item: {
178
+ type: [{ name: string; value: string }];
179
+ expression: string;
180
+ }) => `${item.type[0].value}:${item.expression}`
172
181
  );
173
182
 
174
183
  result.attachments = [...staticAttachments, ...runtimeAttachments];
@@ -1,13 +1,36 @@
1
1
  import { html } from 'lit-html';
2
- import { ActionConfig, COLORS } from '../types';
2
+ import { ActionConfig, COLORS, ValidationResult } from '../types';
3
3
  import { Node, SetContactChannel } from '../../store/flow-definition';
4
4
 
5
5
  export const set_contact_channel: ActionConfig = {
6
- name: 'Update Contact Channel',
6
+ name: 'Update Channel',
7
7
  color: COLORS.update,
8
8
  render: (_node: Node, action: SetContactChannel) => {
9
- return html`<div>
10
- Set contact channel to <b>${action.channel.name}</b>
11
- </div>`;
9
+ return html`<div>Set to <b>${action.channel.name}</b></div>`;
10
+ },
11
+ form: {
12
+ channel: {
13
+ type: 'select',
14
+ label: 'Channel',
15
+ required: true,
16
+ searchable: true,
17
+ clearable: false,
18
+ endpoint: '/api/v2/channels.json',
19
+ valueKey: 'uuid',
20
+ nameKey: 'name',
21
+ helpText: 'Select the channel to set for the contact'
22
+ }
23
+ },
24
+ validate: (formData: SetContactChannel): ValidationResult => {
25
+ const errors: { [key: string]: string } = {};
26
+
27
+ if (!formData.channel) {
28
+ errors.channel = 'Channel is required';
29
+ }
30
+
31
+ return {
32
+ valid: Object.keys(errors).length === 0,
33
+ errors
34
+ };
12
35
  }
13
36
  };
@@ -1,13 +1,67 @@
1
1
  import { html } from 'lit-html';
2
- import { ActionConfig, COLORS } from '../types';
2
+ import { ActionConfig, COLORS, ValidationResult } from '../types';
3
3
  import { Node, SetContactField } from '../../store/flow-definition';
4
4
 
5
5
  export const set_contact_field: ActionConfig = {
6
- name: 'Update Contact Field',
6
+ name: 'Update Field',
7
7
  color: COLORS.update,
8
8
  render: (_node: Node, action: SetContactField) => {
9
9
  return html`<div>
10
10
  Set <b>${action.field.name}</b> to <b>${action.value}</b>
11
11
  </div>`;
12
+ },
13
+ form: {
14
+ field: {
15
+ type: 'select',
16
+ label: 'Field',
17
+ required: true,
18
+ searchable: true,
19
+ clearable: false,
20
+ nameKey: 'name',
21
+ valueKey: 'key',
22
+ endpoint: '/api/v2/fields.json',
23
+ helpText: 'Select the contact field to update',
24
+ allowCreate: true,
25
+ createArbitraryOption: (input: string) => ({ key: input, name: input })
26
+ },
27
+ value: {
28
+ type: 'text',
29
+ label: 'Value',
30
+ placeholder: 'Enter field value...',
31
+ required: true,
32
+ evaluated: true,
33
+ helpText:
34
+ 'The new value for the contact field. You can use expressions like @contact.name'
35
+ }
36
+ },
37
+ fromFormData: (formData: SetContactField): SetContactField => {
38
+ const field = formData.field[0];
39
+ return {
40
+ uuid: formData.uuid,
41
+ type: 'set_contact_field',
42
+ field: { name: field.name, key: field.key },
43
+ value: formData.value
44
+ };
45
+ },
46
+ validate: (formData: SetContactField): ValidationResult => {
47
+ const errors: { [key: string]: string } = {};
48
+
49
+ if (!formData.field) {
50
+ errors.field = 'Field is required';
51
+ }
52
+
53
+ if (!formData.value || formData.value.trim() === '') {
54
+ errors.value = 'Field value is required';
55
+ }
56
+
57
+ return {
58
+ valid: Object.keys(errors).length === 0,
59
+ errors
60
+ };
61
+ },
62
+ sanitize: (formData: SetContactField): void => {
63
+ if (formData.value && typeof formData.value === 'string') {
64
+ formData.value = formData.value.trim();
65
+ }
12
66
  }
13
67
  };