@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
package/src/flow/utils.ts CHANGED
@@ -1,5 +1,14 @@
1
1
  import { html } from 'lit-html';
2
2
  import { NamedObject, FlowPosition } from '../store/flow-definition';
3
+ import { FlowIssue } from '../store/AppState';
4
+
5
+ export function formatIssueMessage(issue: FlowIssue): string {
6
+ if (issue.dependency) {
7
+ const name = issue.dependency.name || issue.dependency.key;
8
+ return `Cannot find a ${issue.dependency.type} for ${name}`;
9
+ }
10
+ return issue.description;
11
+ }
3
12
 
4
13
  const GRID_SIZE = 20;
5
14
 
@@ -346,6 +355,25 @@ export const calculateReflowPositions = (
346
355
  currentBounds.set(b.uuid, { ...b });
347
356
  }
348
357
 
358
+ // A sacred node yields to an existing node at the top of the canvas when
359
+ // the sacred wasn't dropped above it. The existing node keeps its top
360
+ // position and the sacred node moves below instead.
361
+ for (const sacredUuid of [...sacredSet]) {
362
+ const sacred = currentBounds.get(sacredUuid);
363
+ if (!sacred) continue;
364
+
365
+ for (const [uuid, bounds] of currentBounds) {
366
+ if (uuid === sacredUuid || sacredSet.has(uuid)) continue;
367
+ if (!nodesOverlap(sacred, bounds)) continue;
368
+
369
+ if (sacred.top > bounds.top && bounds.top < MIN_NODE_SPACING) {
370
+ sacredSet.delete(sacredUuid);
371
+ sacredSet.add(uuid);
372
+ break;
373
+ }
374
+ }
375
+ }
376
+
349
377
  // Seed the queue with non-sacred nodes that overlap any sacred node
350
378
  const queue: string[] = [];
351
379
  const inQueue = new Set<string>();
@@ -387,11 +415,48 @@ export const calculateReflowPositions = (
387
415
 
388
416
  if (fixedOverlaps.length === 0) continue;
389
417
 
390
- // Try each direction, pick the one with least disruption
418
+ // Determine direction constraints and axis bias from sacred node overlaps
419
+ const sacredOverlaps = fixedOverlaps.filter((f) => sacredSet.has(f.uuid));
420
+ const allowedDirections: Direction[] = [...DIRECTIONS];
421
+ let axisBias: 'vertical' | 'horizontal' | null = null;
422
+
423
+ if (sacredOverlaps.length > 0) {
424
+ // Rule 1: don't move a lower node above the sacred node
425
+ // Rule 2: don't move a right-of node to the left of the sacred node
426
+ for (const sacred of sacredOverlaps) {
427
+ if (collider.top > sacred.top) {
428
+ const idx = allowedDirections.indexOf('up');
429
+ if (idx !== -1) allowedDirections.splice(idx, 1);
430
+ }
431
+ if (collider.left > sacred.left) {
432
+ const idx = allowedDirections.indexOf('left');
433
+ if (idx !== -1) allowedDirections.splice(idx, 1);
434
+ }
435
+ }
436
+
437
+ // Rule 3: bias direction based on overlap shape
438
+ let totalOverlapWidth = 0;
439
+ let totalOverlapHeight = 0;
440
+ for (const sacred of sacredOverlaps) {
441
+ totalOverlapWidth +=
442
+ Math.min(collider.right, sacred.right) -
443
+ Math.max(collider.left, sacred.left);
444
+ totalOverlapHeight +=
445
+ Math.min(collider.bottom, sacred.bottom) -
446
+ Math.max(collider.top, sacred.top);
447
+ }
448
+ if (totalOverlapWidth > totalOverlapHeight) {
449
+ axisBias = 'vertical'; // wide overlap = nodes stacked = prefer up/down
450
+ } else if (totalOverlapHeight > totalOverlapWidth) {
451
+ axisBias = 'horizontal'; // tall overlap = nodes side-by-side = prefer left/right
452
+ }
453
+ }
454
+
455
+ // Try each allowed direction, pick the one with least disruption
391
456
  let bestPos: { left: number; top: number } | null = null;
392
457
  let bestScore = Infinity;
393
458
 
394
- for (const dir of DIRECTIONS) {
459
+ for (const dir of allowedDirections) {
395
460
  const candidate = computeDirectionalClearance(
396
461
  collider,
397
462
  fixedOverlaps,
@@ -423,7 +488,21 @@ export const calculateReflowPositions = (
423
488
  const distance =
424
489
  Math.abs(candidate.left - collider.left) +
425
490
  Math.abs(candidate.top - collider.top);
426
- const score = cascadeCount * 10000 + distance;
491
+
492
+ // When colliding with sacred nodes, use axis bias scoring;
493
+ // for cascading collisions (no sacred overlap), use original scoring
494
+ let score: number;
495
+ if (sacredOverlaps.length > 0) {
496
+ const isVerticalDir = dir === 'up' || dir === 'down';
497
+ const axisMatch =
498
+ axisBias === null ||
499
+ (axisBias === 'vertical' && isVerticalDir) ||
500
+ (axisBias === 'horizontal' && !isVerticalDir);
501
+ const axisPenalty = axisMatch ? 0 : 5000;
502
+ score = cascadeCount * 2000 + axisPenalty + distance;
503
+ } else {
504
+ score = cascadeCount * 10000 + distance;
505
+ }
427
506
 
428
507
  if (score < bestScore) {
429
508
  bestScore = score;
@@ -648,13 +648,15 @@ export class TembaArrayEditor extends BaseListEditor<ListItem> {
648
648
  }
649
649
 
650
650
  .removable .remove-btn {
651
- visibility: hidden;
651
+ opacity: 0.3;
652
652
  cursor: default;
653
+ pointer-events: none;
653
654
  }
654
655
 
655
656
  .removable .drag-handle {
656
- visibility: hidden;
657
+ opacity: 0.3;
657
658
  cursor: default;
659
+ pointer-events: none;
658
660
  }
659
661
 
660
662
  .drag-handle {
@@ -8,8 +8,11 @@ import {
8
8
  CheckboxFieldConfig,
9
9
  MessageEditorFieldConfig,
10
10
  KeyValueFieldConfig,
11
- ArrayFieldConfig
11
+ ArrayFieldConfig,
12
+ MediaFieldConfig
12
13
  } from '../flow/types';
14
+ import { Attachment } from '../interfaces';
15
+ import { DEFAULT_MEDIA_ENDPOINT } from '../utils';
13
16
 
14
17
  /**
15
18
  * FieldRenderer provides a consistent way to render field configurations
@@ -90,6 +93,14 @@ export class FieldRenderer {
90
93
  context
91
94
  );
92
95
 
96
+ case 'media':
97
+ return FieldRenderer.renderMedia(
98
+ fieldName,
99
+ config as MediaFieldConfig,
100
+ value,
101
+ context
102
+ );
103
+
93
104
  default:
94
105
  return html`<div>Unsupported field type: ${(config as any).type}</div>`;
95
106
  }
@@ -345,6 +356,13 @@ export class FieldRenderer {
345
356
  } = context;
346
357
 
347
358
  return html`<div class="form-field">
359
+ ${config.helpText
360
+ ? html`<div
361
+ style="color: #666; font-size: 13px; margin-bottom: 14px;"
362
+ >
363
+ ${config.helpText}
364
+ </div>`
365
+ : ''}
348
366
  <temba-array-editor
349
367
  name="${fieldName}"
350
368
  .label="${showLabel ? config.label : ''}"
@@ -367,6 +385,58 @@ export class FieldRenderer {
367
385
  </div>`;
368
386
  }
369
387
 
388
+ private static urlToAttachments(url: string): Attachment[] {
389
+ if (!url || !url.trim()) return [];
390
+ const filename = url.split('/').pop() || 'recording';
391
+ const ext = filename.split('.').pop()?.toLowerCase() || '';
392
+ const contentTypes: Record<string, string> = {
393
+ mp3: 'audio/mpeg',
394
+ wav: 'audio/wav',
395
+ ogg: 'audio/ogg',
396
+ m4a: 'audio/mp4'
397
+ };
398
+ return [
399
+ {
400
+ uuid: '',
401
+ content_type: contentTypes[ext] || 'audio/mpeg',
402
+ url,
403
+ filename,
404
+ size: 0,
405
+ error: ''
406
+ }
407
+ ];
408
+ }
409
+
410
+ private static renderMedia(
411
+ fieldName: string,
412
+ config: MediaFieldConfig,
413
+ value: any,
414
+ context: FieldRenderContext
415
+ ): TemplateResult {
416
+ const { onChange, showLabel = true } = context;
417
+ const endpoint = config.endpoint || DEFAULT_MEDIA_ENDPOINT;
418
+ const attachments = FieldRenderer.urlToAttachments(value);
419
+
420
+ return html`
421
+ <div>
422
+ ${showLabel && config.label
423
+ ? html`<label
424
+ style="margin-bottom: 5px; margin-left: 4px; display: block; font-weight: 400; font-size: var(--label-size); letter-spacing: 0.05em; line-height: normal; color: var(--color-label, #777);"
425
+ >${config.label}</label
426
+ >`
427
+ : ''}
428
+ <temba-media-picker
429
+ name="${fieldName}"
430
+ accept="${config.accept || ''}"
431
+ endpoint="${endpoint}"
432
+ max="1"
433
+ .attachments="${attachments}"
434
+ @change="${onChange || (() => {})}"
435
+ ></temba-media-picker>
436
+ </div>
437
+ `;
438
+ }
439
+
370
440
  private static renderMessageEditor(
371
441
  fieldName: string,
372
442
  config: MessageEditorFieldConfig,
package/src/interfaces.ts CHANGED
@@ -304,5 +304,6 @@ export enum CustomEventType {
304
304
  NodeSaved = 'temba-node-saved',
305
305
  NodeEditCancelled = 'temba-node-edit-cancelled',
306
306
  FollowSimulation = 'temba-follow-simulation',
307
- ContactClicked = 'temba-contact-clicked'
307
+ ContactClicked = 'temba-contact-clicked',
308
+ ShowIssue = 'temba-show-issue'
308
309
  }
@@ -96,7 +96,7 @@ export class Dialog extends ResizeElement {
96
96
 
97
97
  .dialog-mask .dialog-container {
98
98
  position: relative;
99
- transition: transform var(--transition-speed) var(--bounce),
99
+ transition: transform var(--transition-speed) ease-in-out,
100
100
  opacity ease-in-out calc(var(--transition-speed) - 50ms);
101
101
  border-radius: var(--curvature);
102
102
  box-shadow: 0px 0px 2px 4px rgba(0, 0, 0, 0.06);
@@ -111,6 +111,7 @@ export class Dialog extends ResizeElement {
111
111
  .dialog-body {
112
112
  background: #fff;
113
113
  overflow-y: auto;
114
+ overflow-x: hidden;
114
115
  flex-grow: 1;
115
116
  }
116
117
 
@@ -124,7 +125,7 @@ export class Dialog extends ResizeElement {
124
125
  }
125
126
 
126
127
  .dialog-mask.dialog-animation-end .dialog-container {
127
- transform: scale(1) !important;
128
+ transform: scale(1) translate(0, 0) !important;
128
129
  }
129
130
 
130
131
  .dialog-mask.dialog-ready .dialog-container {
@@ -253,6 +254,12 @@ export class Dialog extends ResizeElement {
253
254
  @property({ attribute: false })
254
255
  onButtonClicked: (button: Button) => void;
255
256
 
257
+ @property({ type: Number })
258
+ originX: number | null = null;
259
+
260
+ @property({ type: Number })
261
+ originY: number | null = null;
262
+
256
263
  scrollOffset: any = 0;
257
264
 
258
265
  public constructor() {
@@ -289,11 +296,49 @@ export class Dialog extends ResizeElement {
289
296
  const body = document.querySelector('body');
290
297
 
291
298
  if (this.open) {
292
- this.animationEnd = true;
293
- window.setTimeout(() => {
294
- this.ready = true;
295
- this.animationEnd = false;
296
- }, 400);
299
+ if (this.originX != null && this.originY != null) {
300
+ // Spring-from-origin animation: measure final position, then
301
+ // set initial transform at click point and transition to center
302
+ const ox = this.originX;
303
+ const oy = this.originY;
304
+ this.originX = null;
305
+ this.originY = null;
306
+
307
+ requestAnimationFrame(() => {
308
+ const container = this.shadowRoot?.querySelector(
309
+ '.dialog-container'
310
+ ) as HTMLElement;
311
+ if (container) {
312
+ const rect = container.getBoundingClientRect();
313
+ const cx = rect.left + rect.width / 2;
314
+ const cy = rect.top + rect.height / 2;
315
+ const dx = ox - cx;
316
+ const dy = oy - cy;
317
+
318
+ // Disable transition so we can set the start position instantly
319
+ container.style.transition = 'none';
320
+ container.style.transform = `translate(${dx}px, ${dy}px) scale(0.2)`;
321
+ // Force reflow to register the start position
322
+ container.getBoundingClientRect();
323
+
324
+ // Re-enable transition and trigger animation to final position
325
+ container.style.transition = '';
326
+ this.animationEnd = true;
327
+ window.setTimeout(() => {
328
+ this.ready = true;
329
+ this.animationEnd = false;
330
+ container.style.transform = '';
331
+ }, 400);
332
+ }
333
+ });
334
+ } else {
335
+ // Default animation (no origin)
336
+ this.animationEnd = true;
337
+ window.setTimeout(() => {
338
+ this.ready = true;
339
+ this.animationEnd = false;
340
+ }, 400);
341
+ }
297
342
 
298
343
  this.scrollOffset = -document.documentElement.scrollTop;
299
344
  body.style.position = 'fixed';
@@ -148,9 +148,22 @@ export class Modax extends RapidElement {
148
148
 
149
149
  @property({ type: Boolean })
150
150
  suspendSubmit = false;
151
+
152
+ @property({ type: Number })
153
+ originX: number | null = null;
154
+
155
+ @property({ type: Number })
156
+ originY: number | null = null;
157
+
151
158
  // private cancelToken: CancelTokenSource;
152
159
 
153
- private handleSlotClicked(): void {
160
+ private handleSlotClicked(event: MouseEvent): void {
161
+ const el = event.currentTarget as Element;
162
+ if (el) {
163
+ const rect = el.getBoundingClientRect();
164
+ this.originX = rect.left + rect.width / 2;
165
+ this.originY = rect.top;
166
+ }
154
167
  this.open = true;
155
168
  }
156
169
 
@@ -173,6 +186,8 @@ export class Modax extends RapidElement {
173
186
  // clear the modal body out when closed, note that js functions declared on the
174
187
  // window will hang around
175
188
  this.setBody('');
189
+ this.originX = null;
190
+ this.originY = null;
176
191
  }
177
192
  }
178
193
  }
@@ -441,11 +456,13 @@ export class Modax extends RapidElement {
441
456
  .header=${this.header}
442
457
  .buttons=${this.buttons}
443
458
  ?open=${this.open}
444
- ?loading=${this.fetching}
459
+ ?loading=${this.fetching && this.originX == null}
445
460
  ?submitting=${this.submitting}
446
461
  ?destructive=${this.isDestructive()}
447
462
  ?noFocus=${true}
448
463
  ?disabled=${this.disabled}
464
+ .originX=${this.originX}
465
+ .originY=${this.originY}
449
466
  @temba-button-clicked=${this.handleDialogClick.bind(this)}
450
467
  @temba-dialog-hidden=${this.handleDialogHidden.bind(this)}
451
468
  >
@@ -159,8 +159,22 @@ export class ContentMenu extends RapidElement {
159
159
  }
160
160
  }
161
161
 
162
+ private getTopCenter(el: Element): { x: number; y: number } {
163
+ const rect = el.getBoundingClientRect();
164
+ return { x: rect.left + rect.width / 2, y: rect.top };
165
+ }
166
+
162
167
  private handleItemClicked(item: ContentMenuItem, event: MouseEvent) {
163
- this.fireCustomEvent(CustomEventType.Selection, { item, event });
168
+ const el = event.currentTarget as Element;
169
+ const origin = el
170
+ ? this.getTopCenter(el)
171
+ : { x: event.clientX, y: event.clientY };
172
+ this.fireCustomEvent(CustomEventType.Selection, {
173
+ item,
174
+ event,
175
+ originX: origin.x,
176
+ originY: origin.y
177
+ });
164
178
  }
165
179
 
166
180
  public render(): TemplateResult {
package/src/locales/es.ts CHANGED
@@ -1,13 +1,18 @@
1
- // Do not modify this file by hand!
2
- // Re-generate this file by running lit-localize
3
-
4
- /* eslint-disable no-irregular-whitespace */
5
- /* eslint-disable @typescript-eslint/no-explicit-any */
6
-
7
- export const templates = {
8
- scf1453991c986b25: `Tab para completar, enter para seleccionar`,
9
- s73b4d70c02f4b4e0: `No options`,
10
- s8f02e3a18ffc083a: `Are not currently in a flow`,
11
- s638236250662c6b3: `Have sent a message in the last`,
12
- s4788ee206c4570c7: `Have not started this flow in the last 90 days`
13
- };
1
+
2
+ // Do not modify this file by hand!
3
+ // Re-generate this file by running lit-localize
4
+
5
+
6
+
7
+
8
+ /* eslint-disable no-irregular-whitespace */
9
+ /* eslint-disable @typescript-eslint/no-explicit-any */
10
+
11
+ export const templates = {
12
+ 'scf1453991c986b25': `Tab para completar, enter para seleccionar`,
13
+ 's73b4d70c02f4b4e0': `No options`,
14
+ 's8f02e3a18ffc083a': `Are not currently in a flow`,
15
+ 's638236250662c6b3': `Have sent a message in the last`,
16
+ 's4788ee206c4570c7': `Have not started this flow in the last 90 days`,
17
+ };
18
+
package/src/locales/fr.ts CHANGED
@@ -1,13 +1,18 @@
1
- // Do not modify this file by hand!
2
- // Re-generate this file by running lit-localize
3
-
4
- /* eslint-disable no-irregular-whitespace */
5
- /* eslint-disable @typescript-eslint/no-explicit-any */
6
-
7
- export const templates = {
8
- s73b4d70c02f4b4e0: `No options`,
9
- scf1453991c986b25: `Tab to complete, enter to select`,
10
- s8f02e3a18ffc083a: `Are not currently in a flow`,
11
- s638236250662c6b3: `Have sent a message in the last`,
12
- s4788ee206c4570c7: `Have not started this flow in the last 90 days`
13
- };
1
+
2
+ // Do not modify this file by hand!
3
+ // Re-generate this file by running lit-localize
4
+
5
+
6
+
7
+
8
+ /* eslint-disable no-irregular-whitespace */
9
+ /* eslint-disable @typescript-eslint/no-explicit-any */
10
+
11
+ export const templates = {
12
+ 's73b4d70c02f4b4e0': `No options`,
13
+ 'scf1453991c986b25': `Tab to complete, enter to select`,
14
+ 's8f02e3a18ffc083a': `Are not currently in a flow`,
15
+ 's638236250662c6b3': `Have sent a message in the last`,
16
+ 's4788ee206c4570c7': `Have not started this flow in the last 90 days`,
17
+ };
18
+
@@ -10,9 +10,18 @@ export const sourceLocale = `en`;
10
10
  * The other locale codes that this application is localized into. Sorted
11
11
  * lexicographically.
12
12
  */
13
- export const targetLocales = [`es`, `fr`, `pt`] as const;
13
+ export const targetLocales = [
14
+ `es`,
15
+ `fr`,
16
+ `pt`,
17
+ ] as const;
14
18
 
15
19
  /**
16
20
  * All valid project locale codes. Sorted lexicographically.
17
21
  */
18
- export const allLocales = [`en`, `es`, `fr`, `pt`] as const;
22
+ export const allLocales = [
23
+ `en`,
24
+ `es`,
25
+ `fr`,
26
+ `pt`,
27
+ ] as const;
package/src/locales/pt.ts CHANGED
@@ -1,13 +1,18 @@
1
- // Do not modify this file by hand!
2
- // Re-generate this file by running lit-localize
3
-
4
- /* eslint-disable no-irregular-whitespace */
5
- /* eslint-disable @typescript-eslint/no-explicit-any */
6
-
7
- export const templates = {
8
- s73b4d70c02f4b4e0: `No options`,
9
- scf1453991c986b25: `Tab to complete, enter to select`,
10
- s8f02e3a18ffc083a: `Are not currently in a flow`,
11
- s638236250662c6b3: `Have sent a message in the last`,
12
- s4788ee206c4570c7: `Have not started this flow in the last 90 days`
13
- };
1
+
2
+ // Do not modify this file by hand!
3
+ // Re-generate this file by running lit-localize
4
+
5
+
6
+
7
+
8
+ /* eslint-disable no-irregular-whitespace */
9
+ /* eslint-disable @typescript-eslint/no-explicit-any */
10
+
11
+ export const templates = {
12
+ 's73b4d70c02f4b4e0': `No options`,
13
+ 'scf1453991c986b25': `Tab to complete, enter to select`,
14
+ 's8f02e3a18ffc083a': `Are not currently in a flow`,
15
+ 's638236250662c6b3': `Have sent a message in the last`,
16
+ 's4788ee206c4570c7': `Have not started this flow in the last 90 days`,
17
+ };
18
+
@@ -5,6 +5,8 @@ import { css, PropertyValueMap } from 'lit';
5
5
  import { property } from 'lit/decorators.js';
6
6
  import { postJSON, fromCookie, generateUUIDv7 } from '../utils';
7
7
  import { getStore } from '../store/Store';
8
+ import { AppState, fromStore, zustand } from '../store/AppState';
9
+ import { FlowDefinition } from '../store/flow-definition';
8
10
  import { CustomEventType } from '../interfaces';
9
11
  import { Chat, ContactEvent, MessageType } from '../display/Chat';
10
12
  import { Events, renderEvent } from '../events/eventRenderers';
@@ -148,6 +150,10 @@ const SIMULATOR_SIZES: Record<string, SimulatorSize> = {
148
150
  export class Simulator extends RapidElement {
149
151
  static get styles() {
150
152
  return css`
153
+ temba-floating-tab {
154
+ --floating-tab-right: 15px;
155
+ }
156
+
151
157
  :host {
152
158
  /* size-specific dimensions are set dynamically via inline styles */
153
159
  --phone-width: 300px;
@@ -688,6 +694,12 @@ export class Simulator extends RapidElement {
688
694
  `;
689
695
  }
690
696
 
697
+ @fromStore(zustand, (state: AppState) => state.flowDefinition)
698
+ private definition!: FlowDefinition;
699
+
700
+ @fromStore(zustand, (state: AppState) => state.viewingRevision)
701
+ private viewingRevision!: boolean;
702
+
691
703
  @property({ type: String })
692
704
  flow = '';
693
705
 
@@ -1117,8 +1129,12 @@ export class Simulator extends RapidElement {
1117
1129
  continue;
1118
1130
  }
1119
1131
 
1120
- // skip msg_created events without a proper msg property
1121
- if (rawEvent.type === 'msg_created' && !(rawEvent as any).msg) {
1132
+ // skip msg_created/ivr_created events without a proper msg property
1133
+ if (
1134
+ (rawEvent.type === 'msg_created' ||
1135
+ rawEvent.type === 'ivr_created') &&
1136
+ !(rawEvent as any).msg
1137
+ ) {
1122
1138
  continue;
1123
1139
  }
1124
1140
 
@@ -1140,7 +1156,8 @@ export class Simulator extends RapidElement {
1140
1156
  this.currentQuickReplies = (event as any).msg.quick_replies;
1141
1157
  }
1142
1158
 
1143
- const isMessage = event.type === 'msg_created';
1159
+ const isMessage =
1160
+ event.type === 'msg_created' || event.type === 'ivr_created';
1144
1161
  const msg = (event as any).msg;
1145
1162
 
1146
1163
  // Check if the event should be displayed.
@@ -1717,6 +1734,10 @@ export class Simulator extends RapidElement {
1717
1734
  }
1718
1735
 
1719
1736
  protected render(): TemplateResult {
1737
+ if (this.viewingRevision || this.definition?.nodes.length === 0) {
1738
+ return html``;
1739
+ }
1740
+
1720
1741
  const config = this.sizeConfig;
1721
1742
 
1722
1743
  // set CSS custom properties dynamically based on size
@@ -1934,7 +1955,7 @@ export class Simulator extends RapidElement {
1934
1955
  icon="simulator"
1935
1956
  label="Phone Simulator"
1936
1957
  color="#10b981"
1937
- order="3"
1958
+ order="4"
1938
1959
  .hidden=${this.isVisible}
1939
1960
  @temba-button-clicked=${this.handleShow}
1940
1961
  ></temba-floating-tab>