@nyaruka/temba-components 0.131.0 → 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 (430) hide show
  1. package/.github/workflows/publish.yml +4 -1
  2. package/CHANGELOG.md +67 -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 +1156 -619
  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/ContactSearch.js +1 -1
  110. package/out-tsc/src/form/ContactSearch.js.map +1 -1
  111. package/out-tsc/src/form/FieldRenderer.js +2 -4
  112. package/out-tsc/src/form/FieldRenderer.js.map +1 -1
  113. package/out-tsc/src/interfaces.js +3 -0
  114. package/out-tsc/src/interfaces.js.map +1 -1
  115. package/out-tsc/src/list/SortableList.js +98 -33
  116. package/out-tsc/src/list/SortableList.js.map +1 -1
  117. package/out-tsc/src/live/ContactChat.js +15 -18
  118. package/out-tsc/src/live/ContactChat.js.map +1 -1
  119. package/out-tsc/src/store/AppState.js +53 -0
  120. package/out-tsc/src/store/AppState.js.map +1 -1
  121. package/out-tsc/src/utils.js +254 -13
  122. package/out-tsc/src/utils.js.map +1 -1
  123. package/out-tsc/temba-modules.js +4 -0
  124. package/out-tsc/temba-modules.js.map +1 -1
  125. package/out-tsc/test/ActionHelper.js +3 -3
  126. package/out-tsc/test/ActionHelper.js.map +1 -1
  127. package/out-tsc/test/NodeHelper.js +6 -3
  128. package/out-tsc/test/NodeHelper.js.map +1 -1
  129. package/out-tsc/test/actions/add_contact_urn.test.js +202 -0
  130. package/out-tsc/test/actions/add_contact_urn.test.js.map +1 -0
  131. package/out-tsc/test/actions/send_broadcast.test.js +148 -0
  132. package/out-tsc/test/actions/send_broadcast.test.js.map +1 -0
  133. package/out-tsc/test/actions/send_email.test.js +17 -23
  134. package/out-tsc/test/actions/send_email.test.js.map +1 -1
  135. package/out-tsc/test/actions/send_msg.test.js +33 -15
  136. package/out-tsc/test/actions/send_msg.test.js.map +1 -1
  137. package/out-tsc/test/actions/start_session.test.js +116 -0
  138. package/out-tsc/test/actions/start_session.test.js.map +1 -0
  139. package/out-tsc/test/nodes/split_by_airtime.test.js +604 -0
  140. package/out-tsc/test/nodes/split_by_airtime.test.js.map +1 -0
  141. package/out-tsc/test/nodes/split_by_contact_field.test.js +387 -0
  142. package/out-tsc/test/nodes/split_by_contact_field.test.js.map +1 -0
  143. package/out-tsc/test/nodes/split_by_expression.test.js +614 -0
  144. package/out-tsc/test/nodes/split_by_expression.test.js.map +1 -0
  145. package/out-tsc/test/nodes/split_by_random.test.js +3 -3
  146. package/out-tsc/test/nodes/split_by_random.test.js.map +1 -1
  147. package/out-tsc/test/nodes/split_by_resthook.test.js +337 -0
  148. package/out-tsc/test/nodes/split_by_resthook.test.js.map +1 -0
  149. package/out-tsc/test/nodes/split_by_run_result.test.js +920 -0
  150. package/out-tsc/test/nodes/split_by_run_result.test.js.map +1 -0
  151. package/out-tsc/test/nodes/split_by_scheme.test.js +399 -0
  152. package/out-tsc/test/nodes/split_by_scheme.test.js.map +1 -0
  153. package/out-tsc/test/nodes/split_by_subflow.test.js +333 -0
  154. package/out-tsc/test/nodes/split_by_subflow.test.js.map +1 -0
  155. package/out-tsc/test/nodes/wait_for_digits.test.js +2 -2
  156. package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -1
  157. package/out-tsc/test/nodes/wait_for_response.test.js +2 -1
  158. package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -1
  159. package/out-tsc/test/temba-action-drag-between-nodes.test.js +252 -0
  160. package/out-tsc/test/temba-action-drag-between-nodes.test.js.map +1 -0
  161. package/out-tsc/test/temba-canvas-menu.test.js +122 -0
  162. package/out-tsc/test/temba-canvas-menu.test.js.map +1 -0
  163. package/out-tsc/test/temba-flow-editor-node.test.js +85 -2
  164. package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
  165. package/out-tsc/test/temba-flow-editor.test.js +7 -8
  166. package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
  167. package/out-tsc/test/temba-node-editor.test.js +3 -1
  168. package/out-tsc/test/temba-node-editor.test.js.map +1 -1
  169. package/out-tsc/test/temba-node-type-selector.test.js +115 -0
  170. package/out-tsc/test/temba-node-type-selector.test.js.map +1 -0
  171. package/out-tsc/test/temba-omnibox.test.js +2 -1
  172. package/out-tsc/test/temba-omnibox.test.js.map +1 -1
  173. package/out-tsc/test/temba-sortable-list.test.js +51 -0
  174. package/out-tsc/test/temba-sortable-list.test.js.map +1 -1
  175. package/out-tsc/test/temba-utils-index.test.js +1 -27
  176. package/out-tsc/test/temba-utils-index.test.js.map +1 -1
  177. package/out-tsc/test/utils.test.js +2 -0
  178. package/out-tsc/test/utils.test.js.map +1 -1
  179. package/package.json +2 -1
  180. package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
  181. package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
  182. package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
  183. package/screenshots/truth/actions/add_contact_groups/editor/multiple-groups.png +0 -0
  184. package/screenshots/truth/actions/add_contact_groups/editor/single-group.png +0 -0
  185. package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
  186. package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
  187. package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
  188. package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
  189. package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
  190. package/screenshots/truth/actions/add_contact_urn/editor/expression-facebook.png +0 -0
  191. package/screenshots/truth/actions/add_contact_urn/editor/expression-phone.png +0 -0
  192. package/screenshots/truth/actions/add_contact_urn/editor/facebook-id.png +0 -0
  193. package/screenshots/truth/actions/add_contact_urn/editor/instagram-handle.png +0 -0
  194. package/screenshots/truth/actions/add_contact_urn/editor/line-id.png +0 -0
  195. package/screenshots/truth/actions/add_contact_urn/editor/phone-number.png +0 -0
  196. package/screenshots/truth/actions/add_contact_urn/editor/telegram-id.png +0 -0
  197. package/screenshots/truth/actions/add_contact_urn/editor/viber-id.png +0 -0
  198. package/screenshots/truth/actions/add_contact_urn/editor/wechat-id.png +0 -0
  199. package/screenshots/truth/actions/add_contact_urn/editor/whatsapp.png +0 -0
  200. package/screenshots/truth/actions/add_contact_urn/render/expression-facebook.png +0 -0
  201. package/screenshots/truth/actions/add_contact_urn/render/expression-phone.png +0 -0
  202. package/screenshots/truth/actions/add_contact_urn/render/facebook-id.png +0 -0
  203. package/screenshots/truth/actions/add_contact_urn/render/instagram-handle.png +0 -0
  204. package/screenshots/truth/actions/add_contact_urn/render/line-id.png +0 -0
  205. package/screenshots/truth/actions/add_contact_urn/render/phone-number.png +0 -0
  206. package/screenshots/truth/actions/add_contact_urn/render/telegram-id.png +0 -0
  207. package/screenshots/truth/actions/add_contact_urn/render/viber-id.png +0 -0
  208. package/screenshots/truth/actions/add_contact_urn/render/wechat-id.png +0 -0
  209. package/screenshots/truth/actions/add_contact_urn/render/whatsapp.png +0 -0
  210. package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
  211. package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
  212. package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
  213. package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
  214. package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
  215. package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
  216. package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
  217. package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
  218. package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
  219. package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
  220. package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
  221. package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
  222. package/screenshots/truth/actions/send_broadcast/editor/contacts-only.png +0 -0
  223. package/screenshots/truth/actions/send_broadcast/editor/groups-and-contacts.png +0 -0
  224. package/screenshots/truth/actions/send_broadcast/editor/groups-only.png +0 -0
  225. package/screenshots/truth/actions/send_broadcast/editor/many-groups.png +0 -0
  226. package/screenshots/truth/actions/send_broadcast/editor/multiline-text.png +0 -0
  227. package/screenshots/truth/actions/send_broadcast/editor/with-attachments.png +0 -0
  228. package/screenshots/truth/actions/send_broadcast/render/contacts-only.png +0 -0
  229. package/screenshots/truth/actions/send_broadcast/render/groups-and-contacts.png +0 -0
  230. package/screenshots/truth/actions/send_broadcast/render/groups-only.png +0 -0
  231. package/screenshots/truth/actions/send_broadcast/render/many-groups.png +0 -0
  232. package/screenshots/truth/actions/send_broadcast/render/multiline-text.png +0 -0
  233. package/screenshots/truth/actions/send_broadcast/render/with-attachments.png +0 -0
  234. package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
  235. package/screenshots/truth/actions/send_email/editor/empty-body.png +0 -0
  236. package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
  237. package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
  238. package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
  239. package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
  240. package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
  241. package/screenshots/truth/actions/send_email/editor/with-expressions.png +0 -0
  242. package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
  243. package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
  244. package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
  245. package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
  246. package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
  247. package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
  248. package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
  249. package/screenshots/truth/actions/send_email/render/with-expressions.png +0 -0
  250. package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
  251. package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
  252. package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
  253. package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
  254. package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
  255. package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
  256. package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
  257. package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
  258. package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
  259. package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
  260. package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
  261. package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
  262. package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
  263. package/screenshots/truth/actions/send_msg/render/text-without-quick-replies.png +0 -0
  264. package/screenshots/truth/actions/start_session/editor/contact-query.png +0 -0
  265. package/screenshots/truth/actions/start_session/editor/contacts-only.png +0 -0
  266. package/screenshots/truth/actions/start_session/editor/create-contact.png +0 -0
  267. package/screenshots/truth/actions/start_session/editor/groups-and-contacts.png +0 -0
  268. package/screenshots/truth/actions/start_session/editor/groups-only.png +0 -0
  269. package/screenshots/truth/actions/start_session/editor/many-recipients.png +0 -0
  270. package/screenshots/truth/actions/start_session/render/contact-query.png +0 -0
  271. package/screenshots/truth/actions/start_session/render/contacts-only.png +0 -0
  272. package/screenshots/truth/actions/start_session/render/create-contact.png +0 -0
  273. package/screenshots/truth/actions/start_session/render/groups-and-contacts.png +0 -0
  274. package/screenshots/truth/actions/start_session/render/groups-only.png +0 -0
  275. package/screenshots/truth/actions/start_session/render/many-recipients.png +0 -0
  276. package/screenshots/truth/canvas-menu/open.png +0 -0
  277. package/screenshots/truth/editor/router.png +0 -0
  278. package/screenshots/truth/editor/wait.png +0 -0
  279. package/screenshots/truth/list/fields-dragging.png +0 -0
  280. package/screenshots/truth/list/sortable-dragging.png +0 -0
  281. package/screenshots/truth/node-type-selector/action-mode.png +0 -0
  282. package/screenshots/truth/node-type-selector/split-mode.png +0 -0
  283. package/screenshots/truth/nodes/split_by_llm/editor/information-extraction.png +0 -0
  284. package/screenshots/truth/nodes/split_by_llm/editor/sentiment-analysis.png +0 -0
  285. package/screenshots/truth/nodes/split_by_llm/editor/summarization.png +0 -0
  286. package/screenshots/truth/nodes/split_by_llm/editor/translation-task.png +0 -0
  287. package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
  288. package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
  289. package/screenshots/truth/nodes/split_by_llm/render/summarization.png +0 -0
  290. package/screenshots/truth/nodes/split_by_llm/render/translation-task.png +0 -0
  291. package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
  292. package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
  293. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  294. package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
  295. package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
  296. package/screenshots/truth/nodes/split_by_llm_categorize/render/basic-categorization.png +0 -0
  297. package/screenshots/truth/nodes/split_by_llm_categorize/render/custom-input-and-result-name.png +0 -0
  298. package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
  299. package/screenshots/truth/nodes/split_by_llm_categorize/render/many-categories.png +0 -0
  300. package/screenshots/truth/nodes/split_by_llm_categorize/render/minimal-categories.png +0 -0
  301. package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
  302. package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
  303. package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
  304. package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
  305. package/screenshots/truth/nodes/split_by_random/render/ab-test-multiple-variants.png +0 -0
  306. package/screenshots/truth/nodes/split_by_random/render/sampling-split.png +0 -0
  307. package/screenshots/truth/nodes/split_by_random/render/three-way-split.png +0 -0
  308. package/screenshots/truth/nodes/split_by_random/render/two-bucket-split.png +0 -0
  309. package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
  310. package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
  311. package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
  312. package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
  313. package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
  314. package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
  315. package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
  316. package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
  317. package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
  318. package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
  319. package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
  320. package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
  321. package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
  322. package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
  323. package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
  324. package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
  325. package/src/Icons.ts +4 -1
  326. package/src/events.ts +2 -6
  327. package/src/flow/CanvasMenu.ts +217 -0
  328. package/src/flow/CanvasNode.ts +408 -10
  329. package/src/flow/Editor.ts +683 -44
  330. package/src/flow/NodeEditor.ts +304 -125
  331. package/src/flow/NodeTypeSelector.ts +592 -0
  332. package/src/flow/actions/add_contact_groups.ts +4 -4
  333. package/src/flow/actions/add_contact_urn.ts +76 -4
  334. package/src/flow/actions/add_input_labels.ts +4 -4
  335. package/src/flow/actions/play_audio.ts +2 -2
  336. package/src/flow/actions/remove_contact_groups.ts +14 -6
  337. package/src/flow/actions/request_optin.ts +2 -2
  338. package/src/flow/actions/say_msg.ts +2 -2
  339. package/src/flow/actions/send_broadcast.ts +85 -23
  340. package/src/flow/actions/send_email.ts +10 -6
  341. package/src/flow/actions/send_msg.ts +22 -32
  342. package/src/flow/actions/set_contact_channel.ts +5 -11
  343. package/src/flow/actions/set_contact_field.ts +20 -25
  344. package/src/flow/actions/set_contact_language.ts +9 -4
  345. package/src/flow/actions/set_contact_name.ts +3 -15
  346. package/src/flow/actions/set_contact_status.ts +3 -3
  347. package/src/flow/actions/set_run_result.ts +4 -4
  348. package/src/flow/actions/start_session.ts +208 -6
  349. package/src/flow/config.ts +13 -15
  350. package/src/flow/currencies.ts +51 -0
  351. package/src/flow/nodes/shared-rules.ts +301 -0
  352. package/src/flow/nodes/shared.ts +18 -0
  353. package/src/flow/nodes/split_by_airtime.ts +238 -5
  354. package/src/flow/nodes/split_by_contact_field.ts +185 -3
  355. package/src/flow/nodes/split_by_expression.ts +94 -2
  356. package/src/flow/nodes/split_by_groups.ts +15 -10
  357. package/src/flow/nodes/split_by_intent.ts +7 -0
  358. package/src/flow/nodes/split_by_llm.ts +4 -3
  359. package/src/flow/nodes/split_by_llm_categorize.ts +4 -4
  360. package/src/flow/nodes/split_by_random.ts +5 -5
  361. package/src/flow/nodes/split_by_resthook.ts +130 -0
  362. package/src/flow/nodes/split_by_run_result.ts +249 -3
  363. package/src/flow/nodes/split_by_scheme.ts +192 -2
  364. package/src/flow/nodes/split_by_subflow.ts +6 -4
  365. package/src/flow/nodes/split_by_ticket.ts +4 -3
  366. package/src/flow/nodes/split_by_webhook.ts +6 -5
  367. package/src/flow/nodes/wait_for_audio.ts +2 -2
  368. package/src/flow/nodes/wait_for_digits.ts +2 -2
  369. package/src/flow/nodes/wait_for_image.ts +2 -2
  370. package/src/flow/nodes/wait_for_location.ts +2 -2
  371. package/src/flow/nodes/wait_for_menu.ts +2 -2
  372. package/src/flow/nodes/wait_for_response.ts +48 -679
  373. package/src/flow/nodes/wait_for_video.ts +2 -2
  374. package/src/flow/types.ts +109 -23
  375. package/src/flow/utils.ts +108 -14
  376. package/src/form/ContactSearch.ts +1 -1
  377. package/src/form/FieldRenderer.ts +2 -4
  378. package/src/interfaces.ts +3 -0
  379. package/src/list/SortableList.ts +109 -34
  380. package/src/live/ContactChat.ts +15 -18
  381. package/src/store/AppState.ts +69 -0
  382. package/src/store/flow-definition.d.ts +2 -5
  383. package/src/utils.ts +332 -12
  384. package/static/api/channels.json +46 -0
  385. package/static/api/resthooks.json +31 -0
  386. package/static/svg/index.svg +1 -1
  387. package/static/svg/work/traced/lightning-02.svg +1 -0
  388. package/static/svg/work/used/lightning-02.svg +3 -0
  389. package/temba-modules.ts +4 -0
  390. package/test/ActionHelper.ts +3 -3
  391. package/test/NodeHelper.ts +6 -3
  392. package/test/actions/add_contact_urn.test.ts +287 -0
  393. package/test/actions/send_broadcast.test.ts +190 -0
  394. package/test/actions/send_email.test.ts +17 -23
  395. package/test/actions/send_msg.test.ts +39 -15
  396. package/test/actions/start_session.test.ts +151 -0
  397. package/test/nodes/split_by_airtime.test.ts +673 -0
  398. package/test/nodes/split_by_contact_field.test.ts +451 -0
  399. package/test/nodes/split_by_expression.test.ts +751 -0
  400. package/test/nodes/split_by_random.test.ts +3 -3
  401. package/test/nodes/split_by_resthook.test.ts +398 -0
  402. package/test/nodes/split_by_run_result.test.ts +1109 -0
  403. package/test/nodes/split_by_scheme.test.ts +486 -0
  404. package/test/nodes/split_by_subflow.test.ts +381 -0
  405. package/test/nodes/wait_for_digits.test.ts +2 -2
  406. package/test/nodes/wait_for_response.test.ts +2 -1
  407. package/test/temba-action-drag-between-nodes.test.ts +301 -0
  408. package/test/temba-canvas-menu.test.ts +156 -0
  409. package/test/temba-flow-editor-node.test.ts +102 -2
  410. package/test/temba-flow-editor.test.ts +7 -8
  411. package/test/temba-node-editor.test.ts +3 -1
  412. package/test/temba-node-type-selector.test.ts +152 -0
  413. package/test/temba-omnibox.test.ts +2 -1
  414. package/test/temba-sortable-list.test.ts +69 -0
  415. package/test/temba-utils-index.test.ts +0 -35
  416. package/test/utils.test.ts +2 -0
  417. package/test-assets/contacts/history.json +14 -20
  418. package/web-dev-server.config.mjs +3 -1
  419. package/out-tsc/src/flow/actions/call_classifier.js +0 -11
  420. package/out-tsc/src/flow/actions/call_classifier.js.map +0 -1
  421. package/out-tsc/src/flow/actions/call_resthook.js +0 -11
  422. package/out-tsc/src/flow/actions/call_resthook.js.map +0 -1
  423. package/out-tsc/src/flow/actions/split_by_expression_example.js +0 -77
  424. package/out-tsc/src/flow/actions/split_by_expression_example.js.map +0 -1
  425. package/out-tsc/src/flow/actions/transfer_airtime.js +0 -11
  426. package/out-tsc/src/flow/actions/transfer_airtime.js.map +0 -1
  427. package/src/flow/actions/call_classifier.ts +0 -12
  428. package/src/flow/actions/call_resthook.ts +0 -12
  429. package/src/flow/actions/split_by_expression_example.ts +0 -88
  430. package/src/flow/actions/transfer_airtime.ts +0 -12
@@ -0,0 +1,592 @@
1
+ import { css, html, TemplateResult } from 'lit';
2
+ import { property, state } from 'lit/decorators.js';
3
+ import { RapidElement } from '../RapidElement';
4
+ import { CustomEventType } from '../interfaces';
5
+ import { NODE_CONFIG, ACTION_CONFIG } from './config';
6
+ import {
7
+ NodeConfig,
8
+ ActionConfig,
9
+ ACTION_GROUPS,
10
+ SPLIT_GROUPS,
11
+ ACTION_GROUP_METADATA,
12
+ SPLIT_GROUP_METADATA
13
+ } from './types';
14
+
15
+ /**
16
+ * Event detail for node type selection
17
+ */
18
+ export interface NodeTypeSelection {
19
+ nodeType: string;
20
+ position: { x: number; y: number };
21
+ }
22
+
23
+ /**
24
+ * Categorizes node types for display
25
+ */
26
+ interface NodeCategory {
27
+ name: string;
28
+ description: string;
29
+ color: string;
30
+ items: Array<{ type: string; config: NodeConfig | ActionConfig }>;
31
+ isBranching?: boolean; // true if this category contains actions that branch/split
32
+ }
33
+
34
+ /**
35
+ * NodeTypeSelector - A dialog for selecting which type of node to create
36
+ * Shows categorized lists of available actions and splits
37
+ */
38
+ export class NodeTypeSelector extends RapidElement {
39
+ static get styles() {
40
+ return css`
41
+ :host {
42
+ position: fixed;
43
+ top: 0;
44
+ left: 0;
45
+ right: 0;
46
+ bottom: 0;
47
+ z-index: 10001;
48
+ display: none;
49
+ }
50
+
51
+ :host([open]) {
52
+ display: flex;
53
+ align-items: center;
54
+ justify-content: center;
55
+ }
56
+
57
+ .overlay {
58
+ position: absolute;
59
+ top: 0;
60
+ left: 0;
61
+ right: 0;
62
+ bottom: 0;
63
+ background: rgba(0, 0, 0, 0.5);
64
+ }
65
+
66
+ .dialog {
67
+ position: relative;
68
+ background: white;
69
+ border-radius: var(--curvature);
70
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
71
+ max-width: 700px;
72
+ max-height: 80vh;
73
+ width: 90%;
74
+ display: flex;
75
+ flex-direction: column;
76
+ }
77
+
78
+ .header {
79
+ padding: 1.5em;
80
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
81
+ }
82
+
83
+ .header h2 {
84
+ margin: 0;
85
+ font-size: 1.5rem;
86
+ font-weight: 600;
87
+ color: var(--color-text-dark);
88
+ }
89
+
90
+ .content {
91
+ overflow-y: auto;
92
+ overflow-x: hidden;
93
+ flex: 1;
94
+ padding: 0;
95
+ }
96
+
97
+ .section-regular {
98
+ padding: 1.5em;
99
+ }
100
+
101
+ .section-branching {
102
+ background: linear-gradient(
103
+ 135deg,
104
+ rgba(170, 170, 170, 0.12),
105
+ rgba(170, 170, 170, 0.08)
106
+ );
107
+ padding: 1.5em;
108
+ margin: 0 -1.5em;
109
+ padding-left: 3em;
110
+ padding-right: 3em;
111
+ }
112
+
113
+ .section-header {
114
+ margin-bottom: 1.5em;
115
+ padding-top: 1em;
116
+ }
117
+
118
+ .section-title {
119
+ font-weight: 700;
120
+ font-size: 1.1rem;
121
+ color: var(--color-text-dark);
122
+ margin-bottom: 0.35em;
123
+ display: flex;
124
+ align-items: center;
125
+ }
126
+
127
+ .section-title::before {
128
+ content: '';
129
+ display: inline-block;
130
+ height: 1.2em;
131
+ background: linear-gradient(
132
+ 135deg,
133
+ var(--color-primary-dark),
134
+ var(--color-primary)
135
+ );
136
+ border-radius: 2px;
137
+ }
138
+
139
+ .section-description {
140
+ font-size: 0.9rem;
141
+ color: var(--color-text);
142
+ opacity: 0.7;
143
+ margin-left: 0em;
144
+ padding-bottom: 1em;
145
+ }
146
+
147
+ .category {
148
+ margin-bottom: 2em;
149
+ }
150
+
151
+ .category:last-child {
152
+ margin-bottom: 0;
153
+ }
154
+
155
+ .category-title {
156
+ font-weight: 600;
157
+ color: var(--color-text-dark);
158
+ margin-bottom: 0.5em;
159
+ text-transform: uppercase;
160
+ letter-spacing: 0.05em;
161
+ font-size: 0.9rem;
162
+ opacity: 0.7;
163
+ }
164
+
165
+ .category-description {
166
+ font-size: 0.85rem;
167
+ color: var(--color-text);
168
+ opacity: 0.6;
169
+ margin-bottom: 0.75em;
170
+ line-height: 1.4;
171
+ }
172
+
173
+ .items-grid {
174
+ display: grid;
175
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
176
+ gap: 0.75em;
177
+ }
178
+
179
+ .node-item {
180
+ padding: 1em;
181
+ border: 1px solid rgba(0, 0, 0, 0.1);
182
+ border-radius: calc(var(--curvature) * 0.75);
183
+ cursor: pointer;
184
+ transition: all 0.15s ease;
185
+ background: white;
186
+ position: relative;
187
+ overflow: hidden;
188
+ }
189
+
190
+ .node-item::before {
191
+ content: '';
192
+ position: absolute;
193
+ top: 0;
194
+ left: 0;
195
+ width: 4px;
196
+ height: 100%;
197
+ background: var(--item-color, rgba(0, 0, 0, 0.1));
198
+ }
199
+
200
+ .node-item:hover {
201
+ border-color: var(--item-color, var(--color-primary));
202
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
203
+ transform: translateY(-1px);
204
+ }
205
+
206
+ .node-item:hover::before {
207
+ width: 6px;
208
+ }
209
+
210
+ .node-item-title {
211
+ font-weight: 500;
212
+ font-size: 1rem;
213
+ color: var(--color-text-dark);
214
+ margin-bottom: 0.25em;
215
+ }
216
+
217
+ .node-item-type {
218
+ font-size: 0.75rem;
219
+ color: var(--color-text);
220
+ opacity: 0.6;
221
+ font-family: monospace;
222
+ }
223
+
224
+ .footer {
225
+ padding: 1em 1.5em;
226
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
227
+ display: flex;
228
+ justify-content: flex-end;
229
+ }
230
+
231
+ temba-button {
232
+ --button-y: 0.5em;
233
+ --button-x: 1.25em;
234
+ }
235
+ `;
236
+ }
237
+
238
+ @property({ type: Boolean, reflect: true })
239
+ public open = false;
240
+
241
+ @property({ type: String })
242
+ public mode: 'action' | 'split' | 'action-no-branching' = 'action';
243
+
244
+ @state()
245
+ private clickPosition = { x: 0, y: 0 };
246
+
247
+ public show(
248
+ mode: 'action' | 'split' | 'action-no-branching',
249
+ position: { x: number; y: number }
250
+ ) {
251
+ this.mode = mode;
252
+ this.clickPosition = position;
253
+ this.open = true;
254
+ }
255
+
256
+ public close() {
257
+ this.open = false;
258
+ }
259
+
260
+ private handleNodeTypeClick(nodeType: string) {
261
+ this.fireCustomEvent(CustomEventType.Selection, {
262
+ nodeType,
263
+ position: this.clickPosition
264
+ } as NodeTypeSelection);
265
+ this.close();
266
+ }
267
+
268
+ private handleOverlayClick() {
269
+ this.close();
270
+ }
271
+
272
+ private getCategories(): NodeCategory[] {
273
+ if (this.mode === 'action' || this.mode === 'action-no-branching') {
274
+ // Group actions by group
275
+ const actionsByGroup = new Map<
276
+ string,
277
+ Array<{ type: string; config: ActionConfig }>
278
+ >();
279
+ const splitsByGroup = new Map<
280
+ string,
281
+ Array<{ type: string; config: NodeConfig }>
282
+ >();
283
+
284
+ // Collect regular actions (from ACTION_CONFIG, unless hideFromActions is true)
285
+ Object.entries(ACTION_CONFIG)
286
+ .filter(([_, config]) => {
287
+ return config.name && !config.hideFromActions && config.group;
288
+ })
289
+ .forEach(([type, config]) => {
290
+ const group = config.group;
291
+ if (!actionsByGroup.has(group)) {
292
+ actionsByGroup.set(group, []);
293
+ }
294
+ actionsByGroup.get(group)!.push({ type, config });
295
+ });
296
+
297
+ // Collect nodes that have showAsAction=true (these appear as "with split" actions)
298
+ // Only if we're not in 'action-no-branching' mode
299
+ if (this.mode === 'action') {
300
+ Object.entries(NODE_CONFIG)
301
+ .filter(([type, config]) => {
302
+ return (
303
+ type !== 'execute_actions' &&
304
+ config.name &&
305
+ config.showAsAction &&
306
+ config.group
307
+ );
308
+ })
309
+ .forEach(([type, config]) => {
310
+ const group = config.group!;
311
+ if (!splitsByGroup.has(group)) {
312
+ splitsByGroup.set(group, []);
313
+ }
314
+ splitsByGroup.get(group)!.push({ type, config });
315
+ });
316
+ }
317
+
318
+ // Build categories - first regular actions, then splitting actions
319
+ const categories: NodeCategory[] = [];
320
+
321
+ // Get the implicit order from ACTION_GROUPS object
322
+ const actionGroupOrder = Object.keys(ACTION_GROUPS);
323
+ // Get the implicit order of actions from ACTION_CONFIG
324
+ const actionConfigOrder = Object.keys(ACTION_CONFIG);
325
+
326
+ // Add regular action categories sorted by implicit order
327
+ const sortedActionCategories = Array.from(actionsByGroup.entries()).sort(
328
+ ([groupA], [groupB]) => {
329
+ const orderA = actionGroupOrder.indexOf(groupA);
330
+ const orderB = actionGroupOrder.indexOf(groupB);
331
+ return (
332
+ (orderA === -1 ? 999 : orderA) - (orderB === -1 ? 999 : orderB)
333
+ );
334
+ }
335
+ );
336
+
337
+ sortedActionCategories.forEach(([group, items]) => {
338
+ const metadata = ACTION_GROUP_METADATA[group];
339
+ // Sort items within the category by their order in ACTION_CONFIG
340
+ const sortedItems = items.sort((a, b) => {
341
+ const orderA = actionConfigOrder.indexOf(a.type);
342
+ const orderB = actionConfigOrder.indexOf(b.type);
343
+ return (
344
+ (orderA === -1 ? 999 : orderA) - (orderB === -1 ? 999 : orderB)
345
+ );
346
+ });
347
+ categories.push({
348
+ name: metadata.title,
349
+ description: metadata.description,
350
+ color: metadata.color,
351
+ items: sortedItems,
352
+ isBranching: false
353
+ });
354
+ });
355
+
356
+ // Add splitting action categories (with modified description to indicate they split)
357
+ // Also sorted by implicit order
358
+ // Get the implicit order of nodes from NODE_CONFIG
359
+ const nodeConfigOrder = Object.keys(NODE_CONFIG);
360
+
361
+ const sortedSplitCategories = Array.from(splitsByGroup.entries()).sort(
362
+ ([groupA], [groupB]) => {
363
+ const orderA = actionGroupOrder.indexOf(groupA);
364
+ const orderB = actionGroupOrder.indexOf(groupB);
365
+ return (
366
+ (orderA === -1 ? 999 : orderA) - (orderB === -1 ? 999 : orderB)
367
+ );
368
+ }
369
+ );
370
+
371
+ sortedSplitCategories.forEach(([group, items]) => {
372
+ const metadata = ACTION_GROUP_METADATA[group];
373
+ // Sort items within the category by their order in NODE_CONFIG
374
+ const sortedItems = items.sort((a, b) => {
375
+ const orderA = nodeConfigOrder.indexOf(a.type);
376
+ const orderB = nodeConfigOrder.indexOf(b.type);
377
+ return (
378
+ (orderA === -1 ? 999 : orderA) - (orderB === -1 ? 999 : orderB)
379
+ );
380
+ });
381
+ categories.push({
382
+ name: metadata.title,
383
+ description: metadata.description,
384
+ color: metadata.color,
385
+ items: sortedItems,
386
+ isBranching: true
387
+ });
388
+ });
389
+
390
+ return categories;
391
+ } else {
392
+ // Group splits by group
393
+ const itemsByGroup = new Map<
394
+ string,
395
+ Array<{ type: string; config: NodeConfig }>
396
+ >();
397
+
398
+ Object.entries(NODE_CONFIG)
399
+ .filter(([type, config]) => {
400
+ // exclude execute_actions (it's the default action-only node)
401
+ // exclude nodes that have showAsAction=true (they appear in action mode)
402
+ return (
403
+ type !== 'execute_actions' && config.name && !config.showAsAction
404
+ );
405
+ })
406
+ .forEach(([type, config]) => {
407
+ const group = config.group || SPLIT_GROUPS.split;
408
+ if (!itemsByGroup.has(group)) {
409
+ itemsByGroup.set(group, []);
410
+ }
411
+ itemsByGroup.get(group)!.push({ type, config });
412
+ });
413
+
414
+ // Convert to categories using group metadata, sorted by implicit order from SPLIT_GROUPS
415
+ const splitGroupOrder = Object.keys(SPLIT_GROUPS);
416
+ // Get the implicit order of nodes from NODE_CONFIG
417
+ const nodeConfigOrder = Object.keys(NODE_CONFIG);
418
+
419
+ return Array.from(itemsByGroup.entries())
420
+ .map(([group, items]) => {
421
+ const metadata =
422
+ SPLIT_GROUP_METADATA[group] || ACTION_GROUP_METADATA[group];
423
+ // Sort items within the category by their order in NODE_CONFIG
424
+ const sortedItems = items.sort((a, b) => {
425
+ const orderA = nodeConfigOrder.indexOf(a.type);
426
+ const orderB = nodeConfigOrder.indexOf(b.type);
427
+ return (
428
+ (orderA === -1 ? 999 : orderA) - (orderB === -1 ? 999 : orderB)
429
+ );
430
+ });
431
+ return {
432
+ name: metadata.title,
433
+ description: metadata.description,
434
+ color: metadata.color,
435
+ items: sortedItems
436
+ };
437
+ })
438
+ .sort((a, b) => {
439
+ // Find the group key by looking up metadata by title
440
+ const groupA = Object.keys(SPLIT_GROUP_METADATA).find(
441
+ (key) => SPLIT_GROUP_METADATA[key].title === a.name
442
+ )!;
443
+ const groupB = Object.keys(SPLIT_GROUP_METADATA).find(
444
+ (key) => SPLIT_GROUP_METADATA[key].title === b.name
445
+ )!;
446
+ const orderA = splitGroupOrder.indexOf(groupA);
447
+ const orderB = splitGroupOrder.indexOf(groupB);
448
+ return (
449
+ (orderA === -1 ? 999 : orderA) - (orderB === -1 ? 999 : orderB)
450
+ );
451
+ });
452
+ }
453
+ }
454
+
455
+ public render(): TemplateResult {
456
+ if (!this.open) {
457
+ return html``;
458
+ }
459
+
460
+ const categories = this.getCategories();
461
+ const title =
462
+ this.mode === 'split'
463
+ ? 'Select a Split'
464
+ : this.mode === 'action-no-branching'
465
+ ? 'Add Action'
466
+ : 'Select an Action';
467
+
468
+ // Separate regular and branching categories for action mode
469
+ const regularCategories = categories.filter((c) => !c.isBranching);
470
+ const branchingCategories = categories.filter((c) => c.isBranching);
471
+ const hasBranchingSection = branchingCategories.length > 0;
472
+
473
+ return html`
474
+ <div class="overlay" @click=${this.handleOverlayClick}></div>
475
+ <div class="dialog" @click=${(e: Event) => e.stopPropagation()}>
476
+ <div class="header">
477
+ <h2>${title}</h2>
478
+ </div>
479
+ <div class="content">
480
+ ${this.mode === 'action' || this.mode === 'action-no-branching'
481
+ ? html`
482
+ <div class="section-regular">
483
+ ${regularCategories.map(
484
+ (category) => html`
485
+ <div class="category">
486
+ <div class="category-title">${category.name}</div>
487
+ <div class="category-description">
488
+ ${category.description}
489
+ </div>
490
+ <div class="items-grid">
491
+ ${category.items.map(
492
+ (item) => html`
493
+ <div
494
+ class="node-item"
495
+ style="--item-color: ${category.color}"
496
+ @click=${() =>
497
+ this.handleNodeTypeClick(item.type)}
498
+ >
499
+ <div class="node-item-title">
500
+ ${item.config.name}
501
+ </div>
502
+ <div class="node-item-type">${item.type}</div>
503
+ </div>
504
+ `
505
+ )}
506
+ </div>
507
+ </div>
508
+ `
509
+ )}
510
+ </div>
511
+ ${hasBranchingSection
512
+ ? html`
513
+ <div class="section-branching">
514
+ <div class="section-header">
515
+ <div class="section-title">Actions that Branch</div>
516
+ <div class="section-description">
517
+ These actions also split the flow based on their
518
+ outcome
519
+ </div>
520
+ </div>
521
+ ${branchingCategories.map(
522
+ (category) => html`
523
+ <div class="category">
524
+ <div class="category-title">${category.name}</div>
525
+ <div class="category-description">
526
+ ${category.description}
527
+ </div>
528
+ <div class="items-grid">
529
+ ${category.items.map(
530
+ (item) => html`
531
+ <div
532
+ class="node-item"
533
+ style="--item-color: ${category.color}"
534
+ @click=${() =>
535
+ this.handleNodeTypeClick(item.type)}
536
+ >
537
+ <div class="node-item-title">
538
+ ${item.config.name}
539
+ </div>
540
+ <div class="node-item-type">
541
+ ${item.type}
542
+ </div>
543
+ </div>
544
+ `
545
+ )}
546
+ </div>
547
+ </div>
548
+ `
549
+ )}
550
+ </div>
551
+ `
552
+ : ''}
553
+ `
554
+ : html`
555
+ <div class="section-regular">
556
+ ${categories.map(
557
+ (category) => html`
558
+ <div class="category">
559
+ <div class="category-title">${category.name}</div>
560
+ <div class="category-description">
561
+ ${category.description}
562
+ </div>
563
+ <div class="items-grid">
564
+ ${category.items.map(
565
+ (item) => html`
566
+ <div
567
+ class="node-item"
568
+ style="--item-color: ${category.color}"
569
+ @click=${() =>
570
+ this.handleNodeTypeClick(item.type)}
571
+ >
572
+ <div class="node-item-title">
573
+ ${item.config.name}
574
+ </div>
575
+ <div class="node-item-type">${item.type}</div>
576
+ </div>
577
+ `
578
+ )}
579
+ </div>
580
+ </div>
581
+ `
582
+ )}
583
+ </div>
584
+ `}
585
+ </div>
586
+ <div class="footer">
587
+ <temba-button @click=${this.close} secondary>Cancel</temba-button>
588
+ </div>
589
+ </div>
590
+ `;
591
+ }
592
+ }
@@ -1,11 +1,11 @@
1
1
  import { html } from 'lit-html';
2
- import { ActionConfig, COLORS } from '../types';
2
+ import { ActionConfig, ACTION_GROUPS, FormData } from '../types';
3
3
  import { Node, AddToGroup } from '../../store/flow-definition';
4
4
  import { renderNamedObjects } from '../utils';
5
5
 
6
6
  export const add_contact_groups: ActionConfig = {
7
7
  name: 'Add to Group',
8
- color: COLORS.update,
8
+ group: ACTION_GROUPS.contacts,
9
9
  render: (_node: Node, action: AddToGroup) => {
10
10
  return html`<div>${renderNamedObjects(action.groups, 'group')}</div>`;
11
11
  },
@@ -13,7 +13,7 @@ export const add_contact_groups: ActionConfig = {
13
13
  // Form-level transformations - default 1:1 mapping for this case
14
14
  toFormData: (action: AddToGroup) => {
15
15
  return {
16
- groups: action.groups || [],
16
+ groups: action.groups || null,
17
17
  uuid: action.uuid
18
18
  };
19
19
  },
@@ -47,7 +47,7 @@ export const add_contact_groups: ActionConfig = {
47
47
  }
48
48
  }
49
49
  },
50
- fromFormData: (formData: any): AddToGroup => {
50
+ fromFormData: (formData: FormData): AddToGroup => {
51
51
  return {
52
52
  uuid: formData.uuid,
53
53
  type: 'add_contact_groups',