@nyaruka/temba-components 0.129.10 → 0.130.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/demo/components/flow/example.html +5 -1
  3. package/demo/data/flows/sample-flow.json +217 -113
  4. package/dist/temba-components.js +310 -356
  5. package/dist/temba-components.js.map +1 -1
  6. package/out-tsc/src/events.js.map +1 -1
  7. package/out-tsc/src/flow/CanvasNode.js +3 -35
  8. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  9. package/out-tsc/src/flow/Editor.js +9 -6
  10. package/out-tsc/src/flow/Editor.js.map +1 -1
  11. package/out-tsc/src/flow/NodeEditor.js +44 -11
  12. package/out-tsc/src/flow/NodeEditor.js.map +1 -1
  13. package/out-tsc/src/flow/actions/add_contact_groups.js +1 -1
  14. package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
  15. package/out-tsc/src/flow/actions/add_contact_urn.js +1 -1
  16. package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
  17. package/out-tsc/src/flow/actions/add_input_labels.js +1 -1
  18. package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
  19. package/out-tsc/src/flow/actions/remove_contact_groups.js +1 -1
  20. package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -1
  21. package/out-tsc/src/flow/actions/send_email.js +9 -0
  22. package/out-tsc/src/flow/actions/send_email.js.map +1 -1
  23. package/out-tsc/src/flow/actions/send_msg.js +7 -8
  24. package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
  25. package/out-tsc/src/flow/actions/set_contact_channel.js +25 -4
  26. package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
  27. package/out-tsc/src/flow/actions/set_contact_field.js +51 -1
  28. package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
  29. package/out-tsc/src/flow/actions/set_contact_language.js +70 -2
  30. package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
  31. package/out-tsc/src/flow/actions/set_contact_name.js +27 -2
  32. package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
  33. package/out-tsc/src/flow/actions/set_contact_status.js +32 -2
  34. package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
  35. package/out-tsc/src/flow/actions/set_run_result.js +13 -11
  36. package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
  37. package/out-tsc/src/flow/actions/split_by_expression_example.js +4 -4
  38. package/out-tsc/src/flow/actions/split_by_expression_example.js.map +1 -1
  39. package/out-tsc/src/flow/config.js +2 -8
  40. package/out-tsc/src/flow/config.js.map +1 -1
  41. package/out-tsc/src/flow/forms/index.js +2 -0
  42. package/out-tsc/src/flow/forms/index.js.map +1 -0
  43. package/out-tsc/src/flow/nodes/split_by_llm.js +101 -0
  44. package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -0
  45. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +4 -89
  46. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
  47. package/out-tsc/src/flow/nodes/split_by_random.js +117 -0
  48. package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
  49. package/out-tsc/src/flow/nodes/split_by_subflow.js +123 -3
  50. package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
  51. package/out-tsc/src/flow/nodes/split_by_ticket.js +114 -13
  52. package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -1
  53. package/out-tsc/src/flow/nodes/split_by_webhook.js +158 -12
  54. package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
  55. package/out-tsc/src/flow/types.js.map +1 -1
  56. package/out-tsc/src/form/ArrayEditor.js +9 -25
  57. package/out-tsc/src/form/ArrayEditor.js.map +1 -1
  58. package/out-tsc/src/form/FieldRenderer.js +6 -64
  59. package/out-tsc/src/form/FieldRenderer.js.map +1 -1
  60. package/out-tsc/src/form/select/Select.js +29 -58
  61. package/out-tsc/src/form/select/Select.js.map +1 -1
  62. package/out-tsc/src/live/ContactChat.js +48 -66
  63. package/out-tsc/src/live/ContactChat.js.map +1 -1
  64. package/out-tsc/src/utils.js +118 -0
  65. package/out-tsc/src/utils.js.map +1 -1
  66. package/out-tsc/test/nodes/split_by_llm.test.js +174 -0
  67. package/out-tsc/test/nodes/split_by_llm.test.js.map +1 -0
  68. package/out-tsc/test/nodes/split_by_random.test.js +0 -6
  69. package/out-tsc/test/nodes/split_by_random.test.js.map +1 -1
  70. package/out-tsc/test/temba-field-renderer.test.js +6 -3
  71. package/out-tsc/test/temba-field-renderer.test.js.map +1 -1
  72. package/out-tsc/test/utils.test.js +18 -0
  73. package/out-tsc/test/utils.test.js.map +1 -1
  74. package/package.json +1 -1
  75. package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
  76. package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
  77. package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
  78. package/screenshots/truth/actions/add_contact_groups/editor/multiple-groups.png +0 -0
  79. package/screenshots/truth/actions/add_contact_groups/editor/single-group.png +0 -0
  80. package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
  81. package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
  82. package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
  83. package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
  84. package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
  85. package/screenshots/truth/actions/call_llm/editor/information-extraction.png +0 -0
  86. package/screenshots/truth/actions/call_llm/editor/sentiment-analysis.png +0 -0
  87. package/screenshots/truth/actions/call_llm/editor/summarization.png +0 -0
  88. package/screenshots/truth/actions/call_llm/editor/translation-task.png +0 -0
  89. package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
  90. package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
  91. package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
  92. package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
  93. package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
  94. package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
  95. package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
  96. package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
  97. package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
  98. package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
  99. package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
  100. package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
  101. package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
  102. package/screenshots/truth/actions/send_email/editor/empty-body.png +0 -0
  103. package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
  104. package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
  105. package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
  106. package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
  107. package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
  108. package/screenshots/truth/actions/send_email/editor/with-expressions.png +0 -0
  109. package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
  110. package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
  111. package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
  112. package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
  113. package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
  114. package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
  115. package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
  116. package/screenshots/truth/checkbox/checkbox-label-background-hover.png +0 -0
  117. package/screenshots/truth/checkbox/checkbox-no-label-no-background-hover.png +0 -0
  118. package/screenshots/truth/checkbox/checkbox-whitespace-label-no-background-hover.png +0 -0
  119. package/screenshots/truth/checkbox/checkbox-with-help-text.png +0 -0
  120. package/screenshots/truth/checkbox/checked.png +0 -0
  121. package/screenshots/truth/checkbox/default.png +0 -0
  122. package/screenshots/truth/colorpicker/default.png +0 -0
  123. package/screenshots/truth/colorpicker/focused.png +0 -0
  124. package/screenshots/truth/colorpicker/initialized.png +0 -0
  125. package/screenshots/truth/colorpicker/selected.png +0 -0
  126. package/screenshots/truth/compose/attachments-tab.png +0 -0
  127. package/screenshots/truth/compose/attachments-with-files.png +0 -0
  128. package/screenshots/truth/compose/intial-text.png +0 -0
  129. package/screenshots/truth/compose/no-counter.png +0 -0
  130. package/screenshots/truth/compose/wraps-text-and-spaces.png +0 -0
  131. package/screenshots/truth/compose/wraps-text-and-url.png +0 -0
  132. package/screenshots/truth/compose/wraps-text-no-spaces.png +0 -0
  133. package/screenshots/truth/contacts/chat-failure.png +0 -0
  134. package/screenshots/truth/contacts/chat-for-active-contact.png +0 -0
  135. package/screenshots/truth/contacts/chat-sends-attachments-only.png +0 -0
  136. package/screenshots/truth/contacts/chat-sends-text-and-attachments.png +0 -0
  137. package/screenshots/truth/contacts/chat-sends-text-only.png +0 -0
  138. package/screenshots/truth/counter/summary.png +0 -0
  139. package/screenshots/truth/counter/text.png +0 -0
  140. package/screenshots/truth/counter/unicode-variables.png +0 -0
  141. package/screenshots/truth/counter/unicode.png +0 -0
  142. package/screenshots/truth/counter/variable.png +0 -0
  143. package/screenshots/truth/datepicker/date-truncated-time.png +0 -0
  144. package/screenshots/truth/datepicker/date.png +0 -0
  145. package/screenshots/truth/datepicker/initial-timezone.png +0 -0
  146. package/screenshots/truth/datepicker/range-picker-editing-start.png +0 -0
  147. package/screenshots/truth/datepicker/updated-keyboard-date.png +0 -0
  148. package/screenshots/truth/dialog/focused.png +0 -0
  149. package/screenshots/truth/dropdown/right-edge-collision.png +0 -0
  150. package/screenshots/truth/editor/router.png +0 -0
  151. package/screenshots/truth/editor/send_msg.png +0 -0
  152. package/screenshots/truth/editor/set_contact_language.png +0 -0
  153. package/screenshots/truth/editor/set_contact_name.png +0 -0
  154. package/screenshots/truth/editor/set_run_result.png +0 -0
  155. package/screenshots/truth/editor/wait.png +0 -0
  156. package/screenshots/truth/field-renderer/checkbox-checked.png +0 -0
  157. package/screenshots/truth/field-renderer/checkbox-unchecked.png +0 -0
  158. package/screenshots/truth/field-renderer/checkbox-with-errors.png +0 -0
  159. package/screenshots/truth/field-renderer/context-comparison.png +0 -0
  160. package/screenshots/truth/field-renderer/key-value-with-label.png +0 -0
  161. package/screenshots/truth/field-renderer/message-editor-with-label.png +0 -0
  162. package/screenshots/truth/field-renderer/select-multi.png +0 -0
  163. package/screenshots/truth/field-renderer/select-no-label.png +0 -0
  164. package/screenshots/truth/field-renderer/select-with-label.png +0 -0
  165. package/screenshots/truth/field-renderer/text-evaluated.png +0 -0
  166. package/screenshots/truth/field-renderer/text-no-label.png +0 -0
  167. package/screenshots/truth/field-renderer/text-with-errors.png +0 -0
  168. package/screenshots/truth/field-renderer/text-with-label.png +0 -0
  169. package/screenshots/truth/field-renderer/textarea-evaluated.png +0 -0
  170. package/screenshots/truth/field-renderer/textarea-with-label.png +0 -0
  171. package/screenshots/truth/integration/checkbox-markdown-errors.png +0 -0
  172. package/screenshots/truth/list/fields-dragging.png +0 -0
  173. package/screenshots/truth/list/fields-filtered.png +0 -0
  174. package/screenshots/truth/list/fields-hovered.png +0 -0
  175. package/screenshots/truth/list/fields.png +0 -0
  176. package/screenshots/truth/list/items-selected.png +0 -0
  177. package/screenshots/truth/list/items-updated.png +0 -0
  178. package/screenshots/truth/list/items.png +0 -0
  179. package/screenshots/truth/menu/menu-focused-with items.png +0 -0
  180. package/screenshots/truth/menu/menu-refresh-1.png +0 -0
  181. package/screenshots/truth/menu/menu-refresh-2.png +0 -0
  182. package/screenshots/truth/menu/menu-submenu.png +0 -0
  183. package/screenshots/truth/menu/menu-tasks-nextup.png +0 -0
  184. package/screenshots/truth/menu/menu-tasks.png +0 -0
  185. package/screenshots/truth/message-editor/autogrow-initial-content.png +0 -0
  186. package/screenshots/truth/message-editor/default.png +0 -0
  187. package/screenshots/truth/message-editor/drag-highlight.png +0 -0
  188. package/screenshots/truth/message-editor/filtered-attachments.png +0 -0
  189. package/screenshots/truth/message-editor/with-completion.png +0 -0
  190. package/screenshots/truth/message-editor/with-properties.png +0 -0
  191. package/screenshots/truth/modax/form.png +0 -0
  192. package/screenshots/truth/modax/simple.png +0 -0
  193. package/screenshots/truth/nodes/split_by_llm/editor/information-extraction.png +0 -0
  194. package/screenshots/truth/nodes/split_by_llm/editor/sentiment-analysis.png +0 -0
  195. package/screenshots/truth/nodes/split_by_llm/editor/summarization.png +0 -0
  196. package/screenshots/truth/nodes/split_by_llm/editor/translation-task.png +0 -0
  197. package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
  198. package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
  199. package/screenshots/truth/nodes/split_by_llm/render/summarization.png +0 -0
  200. package/screenshots/truth/nodes/split_by_llm/render/translation-task.png +0 -0
  201. package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
  202. package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
  203. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  204. package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
  205. package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
  206. package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
  207. package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
  208. package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
  209. package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
  210. package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
  211. package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
  212. package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
  213. package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
  214. package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
  215. package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
  216. package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
  217. package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
  218. package/screenshots/truth/omnibox/selected.png +0 -0
  219. package/screenshots/truth/options/block.png +0 -0
  220. package/screenshots/truth/run-list/basic.png +0 -0
  221. package/screenshots/truth/select/disabled-multi-selection.png +0 -0
  222. package/screenshots/truth/select/disabled-selection.png +0 -0
  223. package/screenshots/truth/select/disabled.png +0 -0
  224. package/screenshots/truth/select/embedded.png +0 -0
  225. package/screenshots/truth/select/empty-options.png +0 -0
  226. package/screenshots/truth/select/expression-selected.png +0 -0
  227. package/screenshots/truth/select/expressions.png +0 -0
  228. package/screenshots/truth/select/functions.png +0 -0
  229. package/screenshots/truth/select/local-options.png +0 -0
  230. package/screenshots/truth/select/multi-with-endpoint.png +0 -0
  231. package/screenshots/truth/select/multiple-initial-values.png +0 -0
  232. package/screenshots/truth/select/remote-options.png +0 -0
  233. package/screenshots/truth/select/search-enabled.png +0 -0
  234. package/screenshots/truth/select/search-multi-no-matches.png +0 -0
  235. package/screenshots/truth/select/search-selected-focus.png +0 -0
  236. package/screenshots/truth/select/search-selected.png +0 -0
  237. package/screenshots/truth/select/search-with-selected.png +0 -0
  238. package/screenshots/truth/select/searching.png +0 -0
  239. package/screenshots/truth/select/selected-multi-maxitems-reached.png +0 -0
  240. package/screenshots/truth/select/selected-multi.png +0 -0
  241. package/screenshots/truth/select/selected-single.png +0 -0
  242. package/screenshots/truth/select/selection-clearable.png +0 -0
  243. package/screenshots/truth/select/static-initial-value.png +0 -0
  244. package/screenshots/truth/select/static-initial-via-selected.png +0 -0
  245. package/screenshots/truth/select/truncated-selection.png +0 -0
  246. package/screenshots/truth/select/with-placeholder.png +0 -0
  247. package/screenshots/truth/select/without-placeholder.png +0 -0
  248. package/screenshots/truth/slider/update-slider-on-circle-dragged.png +0 -0
  249. package/screenshots/truth/templates/default.png +0 -0
  250. package/screenshots/truth/templates/unapproved.png +0 -0
  251. package/screenshots/truth/textinput/autogrow-initial.png +0 -0
  252. package/screenshots/truth/textinput/input-disabled.png +0 -0
  253. package/screenshots/truth/textinput/input-focused.png +0 -0
  254. package/screenshots/truth/textinput/input-form.png +0 -0
  255. package/screenshots/truth/textinput/input-inserted.png +0 -0
  256. package/screenshots/truth/textinput/input-placeholder.png +0 -0
  257. package/screenshots/truth/textinput/input-updated.png +0 -0
  258. package/screenshots/truth/textinput/input.png +0 -0
  259. package/screenshots/truth/textinput/textarea-focused.png +0 -0
  260. package/screenshots/truth/textinput/textarea.png +0 -0
  261. package/src/events.ts +4 -2
  262. package/src/flow/CanvasNode.ts +2 -39
  263. package/src/flow/Editor.ts +6 -3
  264. package/src/flow/NodeEditor.ts +54 -13
  265. package/src/flow/actions/add_contact_groups.ts +1 -1
  266. package/src/flow/actions/add_contact_urn.ts +1 -1
  267. package/src/flow/actions/add_input_labels.ts +1 -1
  268. package/src/flow/actions/remove_contact_groups.ts +1 -1
  269. package/src/flow/actions/send_email.ts +11 -1
  270. package/src/flow/actions/send_msg.ts +20 -11
  271. package/src/flow/actions/set_contact_channel.ts +28 -5
  272. package/src/flow/actions/set_contact_field.ts +56 -2
  273. package/src/flow/actions/set_contact_language.ts +74 -3
  274. package/src/flow/actions/set_contact_name.ts +31 -3
  275. package/src/flow/actions/set_contact_status.ts +36 -3
  276. package/src/flow/actions/set_run_result.ts +13 -15
  277. package/src/flow/actions/split_by_expression_example.ts +4 -4
  278. package/src/flow/config.ts +2 -8
  279. package/src/flow/forms/index.ts +1 -0
  280. package/src/flow/nodes/split_by_llm.ts +119 -0
  281. package/src/flow/nodes/split_by_llm_categorize.ts +13 -116
  282. package/src/flow/nodes/split_by_random.ts +148 -0
  283. package/src/flow/nodes/split_by_subflow.ts +153 -3
  284. package/src/flow/nodes/split_by_ticket.ts +134 -12
  285. package/src/flow/nodes/split_by_webhook.ts +185 -12
  286. package/src/flow/types.ts +2 -1
  287. package/src/form/ArrayEditor.ts +6 -20
  288. package/src/form/FieldRenderer.ts +6 -65
  289. package/src/form/select/Select.ts +34 -66
  290. package/src/live/ContactChat.ts +56 -58
  291. package/src/store/flow-definition.d.ts +8 -2
  292. package/src/utils.ts +196 -0
  293. package/static/api/fields.json +93 -1208
  294. package/static/api/workspace.json +23 -0
  295. package/test/nodes/split_by_llm.test.ts +232 -0
  296. package/test/nodes/split_by_random.test.ts +0 -7
  297. package/test/temba-field-renderer.test.ts +26 -13
  298. package/test/utils.test.ts +20 -0
  299. package/test-assets/style.css +36 -234
  300. package/web-dev-server.config.mjs +28 -0
  301. package/web-test-runner.config.mjs +38 -1
  302. package/out-tsc/src/flow/actions/call_llm.js +0 -64
  303. package/out-tsc/src/flow/actions/call_llm.js.map +0 -1
  304. package/out-tsc/src/flow/actions/call_webhook.js +0 -131
  305. package/out-tsc/src/flow/actions/call_webhook.js.map +0 -1
  306. package/out-tsc/src/flow/actions/enter_flow.js +0 -14
  307. package/out-tsc/src/flow/actions/enter_flow.js.map +0 -1
  308. package/out-tsc/src/flow/actions/open_ticket.js +0 -73
  309. package/out-tsc/src/flow/actions/open_ticket.js.map +0 -1
  310. package/out-tsc/test/actions/call_llm.test.js +0 -103
  311. package/out-tsc/test/actions/call_llm.test.js.map +0 -1
  312. package/src/flow/actions/call_llm.ts +0 -66
  313. package/src/flow/actions/call_webhook.ts +0 -143
  314. package/src/flow/actions/enter_flow.ts +0 -15
  315. package/src/flow/actions/open_ticket.ts +0 -83
  316. package/test/actions/call_llm.test.ts +0 -137
@@ -1,6 +1,6 @@
1
1
  import { html, css, TemplateResult } from 'lit';
2
2
  import { customElement, property } from 'lit/decorators.js';
3
- import { FieldConfig, SelectFieldConfig } from '../flow/types';
3
+ import { FieldConfig } from '../flow/types';
4
4
  import { BaseListEditor, ListItem } from './BaseListEditor';
5
5
  import { FieldRenderer } from './FieldRenderer';
6
6
 
@@ -116,12 +116,13 @@ export class TembaArrayEditor extends BaseListEditor<ListItem> {
116
116
  }
117
117
 
118
118
  // For select fields, ensure we return the right type
119
- if (config.type === 'select') {
119
+ /*if (config.type === 'select') {
120
+ console.log('computeFieldValue select', currentValue, config);
120
121
  const selectConfig = config as SelectFieldConfig;
121
122
  if (currentValue === undefined || currentValue === null) {
122
123
  return selectConfig.multi ? [] : '';
123
124
  }
124
- }
125
+ }*/
125
126
 
126
127
  return currentValue;
127
128
  }
@@ -144,23 +145,8 @@ export class TembaArrayEditor extends BaseListEditor<ListItem> {
144
145
 
145
146
  // Handle different field types and their change events
146
147
  if (config.type === 'select') {
147
- // For temba-select, extract the correct value
148
- if (target.tagName === 'TEMBA-SELECT') {
149
- if (target.multi || target.emails || target.tags) {
150
- value = target.values || [];
151
- } else {
152
- // Single select: extract value from first selected option
153
- const values = target.values || [];
154
- value =
155
- values.length > 0 && values[0]
156
- ? values[0].value !== undefined
157
- ? values[0].value
158
- : values[0]
159
- : '';
160
- }
161
- } else {
162
- value = target.value;
163
- }
148
+ // Use consistent temba-select value normalization
149
+ value = target.values;
164
150
  } else {
165
151
  // For other field types, use the target value directly
166
152
  value = target.value;
@@ -207,73 +207,17 @@ export class FieldRenderer {
207
207
  style
208
208
  } = context;
209
209
 
210
- // Ensure proper value handling for multi vs single select
211
- const normalizedValue = (() => {
212
- if (config.multi) {
213
- // Multi-select: ensure we have an array and convert strings to option objects
214
- const valueArray = Array.isArray(value) ? value : value ? [value] : [];
215
- return valueArray.map((val) => {
216
- if (typeof val === 'string') {
217
- // Convert string values to option objects
218
- return { name: val, value: val };
219
- }
220
- return val;
221
- });
222
- } else {
223
- // Single select: use the value as-is
224
- return value || '';
225
- }
226
- })();
227
-
228
- if (typeof normalizedValue === 'string') {
229
- return html`<temba-select
230
- name="${fieldName}"
231
- ?required="${config.required}"
232
- .errors="${errors}"
233
- value="${config.multi ? '' : normalizedValue}"
234
- .values="${config.multi ? normalizedValue : undefined}"
235
- ?multi="${config.multi}"
236
- ?searchable="${config.searchable}"
237
- ?tags="${config.tags}"
238
- ?emails="${config.emails}"
239
- ?clearable="${config.clearable || false}"
240
- label="${showLabel ? config.label : ''}"
241
- placeholder="${config.placeholder || ''}"
242
- maxItems="${config.maxItems || 0}"
243
- valueKey="${config.valueKey || 'value'}"
244
- nameKey="${config.nameKey || 'name'}"
245
- endpoint="${config.endpoint || ''}"
246
- .helpText="${config.helpText || ''}"
247
- flavor="${flavor || config.flavor || 'small'}"
248
- class="${extraClasses}"
249
- style="${style}"
250
- .getName=${config.getName}
251
- .createArbitraryOption=${config.createArbitraryOption}
252
- ?allowCreate="${config.allowCreate || false}"
253
- @change="${onChange || (() => {})}"
254
- >
255
- ${config.options?.map((option: any) => {
256
- if (typeof option === 'string') {
257
- return html`<temba-option
258
- name="${option}"
259
- value="${option}"
260
- ></temba-option>`;
261
- } else {
262
- return html`<temba-option
263
- name="${option.label || option.name}"
264
- value="${option.value}"
265
- ></temba-option>`;
266
- }
267
- })}
268
- </temba-select>`;
269
- }
210
+ // Get options - use dynamic options if available, otherwise use static options
211
+ const optionsToRender = config.getDynamicOptions
212
+ ? config.getDynamicOptions()
213
+ : config.options;
270
214
 
271
215
  return html`<temba-select
272
216
  name="${fieldName}"
273
217
  label="${showLabel ? config.label : ''}"
274
218
  ?required="${config.required}"
275
219
  .errors="${errors}"
276
- .values="${normalizedValue}"
220
+ .values=${value}
277
221
  ?multi="${config.multi}"
278
222
  ?searchable="${config.searchable}"
279
223
  ?tags="${config.tags}"
@@ -293,7 +237,7 @@ export class FieldRenderer {
293
237
  ?allowCreate="${config.allowCreate || false}"
294
238
  @change="${onChange || (() => {})}"
295
239
  >
296
- ${config.options?.map((option: any) => {
240
+ ${optionsToRender?.map((option: any) => {
297
241
  if (typeof option === 'string') {
298
242
  return html`<temba-option
299
243
  name="${option}"
@@ -445,9 +389,6 @@ export class FieldRenderer {
445
389
  }
446
390
  }
447
391
 
448
- /**
449
- * Context object for field rendering that provides additional options
450
- */
451
392
  export interface FieldRenderContext {
452
393
  /** Array of error messages for the field */
453
394
  errors?: string[];
@@ -592,6 +592,10 @@ export class Select<T extends SelectOption> extends FieldElement {
592
592
  this.prepareOptionsDefault = this.prepareOptionsDefault.bind(this);
593
593
  this.isMatchDefault = this.isMatchDefault.bind(this);
594
594
  this.handleOrderChanged = this.handleOrderChanged.bind(this);
595
+
596
+ this.createArbitraryOption = (
597
+ this.createArbitraryOption || this.createArbitraryOptionDefault
598
+ ).bind(this);
595
599
  }
596
600
 
597
601
  public prepareOptionsDefault(options: T[]): T[] {
@@ -701,17 +705,6 @@ export class Select<T extends SelectOption> extends FieldElement {
701
705
  );
702
706
  }
703
707
 
704
- public async createOptionPost(payload: any) {
705
- return postJSON(this.endpoint, payload).then((response) => {
706
- if (response.status >= 200 && response.status < 300) {
707
- return {
708
- json: response.json,
709
- payload
710
- };
711
- }
712
- });
713
- }
714
-
715
708
  public updated(changes: Map<string, any>) {
716
709
  super.updated(changes);
717
710
 
@@ -727,60 +720,8 @@ export class Select<T extends SelectOption> extends FieldElement {
727
720
 
728
721
  if (changes.has('values')) {
729
722
  this.updateInputs();
730
-
731
723
  if (this.hasChanges(changes.get('values'))) {
732
- const materialized = [];
733
-
734
- // see if we need to materialize anything
735
- if (this.allowCreate) {
736
- // arbitrary values need to be posted
737
- const arbitraryValues = this.values.filter((value) => {
738
- return (value as any).arbitrary;
739
- });
740
-
741
- for (const value of arbitraryValues) {
742
- if ((value as any).arbitrary) {
743
- materialized.push(this.createOptionPost(value));
744
- }
745
- }
746
-
747
- // update our created values
748
- Promise.all(materialized).then((responses) => {
749
- for (const response of responses) {
750
- if (response) {
751
- // find the value that matches our payload
752
- const original = arbitraryValues.find((value) => {
753
- return value === response.payload;
754
- }) as any;
755
-
756
- if (original) {
757
- // remove our arbitrary flag
758
- delete original.arbitrary;
759
-
760
- // add in the new values from our respones.json
761
- if (response.json) {
762
- for (const key in response.json) {
763
- original[key] = response.json[key];
764
- }
765
- }
766
- }
767
- }
768
- }
769
-
770
- // remove any arbitrary values
771
- for (let i = this.values.length - 1; i >= 0; i--) {
772
- if ((this.values[i] as any).arbitrary) {
773
- this.values.splice(i, 1);
774
- }
775
- }
776
-
777
- // reset our cache
778
- this.cacheKey = new Date().getTime().toString();
779
- this.fireEvent('change');
780
- });
781
- } else {
782
- this.fireEvent('change');
783
- }
724
+ this.fireEvent('change');
784
725
  }
785
726
  }
786
727
 
@@ -955,19 +896,31 @@ export class Select<T extends SelectOption> extends FieldElement {
955
896
  }
956
897
 
957
898
  const selected = event.detail.selected;
899
+
958
900
  // check if we should post it
959
- if (selected.post && this.endpoint) {
901
+ if (selected.arbitrary && this.allowCreate && this.endpoint) {
902
+ this.resolving = true;
960
903
  postJSON(this.endpoint, selected).then((response) => {
961
904
  if (response.status >= 200 && response.status < 300) {
962
905
  this.setSelectedOption(response.json);
963
906
  this.lruCache = lru(20, 60000);
907
+ this.errors = [];
964
908
  } else {
965
- // TODO: find a way to share inline errors
909
+ this.setSelectedOption(selected);
910
+ setTimeout(() => {
911
+ this.errors = [
912
+ 'There was an error creating "' +
913
+ this.getNameInternal(selected) +
914
+ '"'
915
+ ];
916
+ }, 0);
966
917
  this.blur();
967
918
  }
919
+ this.resolving = false;
968
920
  });
969
921
  } else {
970
922
  this.setSelectedOption(selected);
923
+ this.errors = [];
971
924
  }
972
925
  }
973
926
 
@@ -1009,6 +962,21 @@ export class Select<T extends SelectOption> extends FieldElement {
1009
962
  public handleRemoveSelection(selectionToRemove: any): void {
1010
963
  this.removeValue(selectionToRemove);
1011
964
  this.visibleOptions = [];
965
+ this.errors = [];
966
+
967
+ // if we allow create, double check our values
968
+ if (this.allowCreate) {
969
+ const arbitrary = this.values.find((v) => v.arbitrary);
970
+ if (arbitrary) {
971
+ setTimeout(() => {
972
+ this.errors = [
973
+ 'There was an error creating "' +
974
+ this.getNameInternal(arbitrary) +
975
+ '"'
976
+ ];
977
+ }, 0);
978
+ }
979
+ }
1012
980
  }
1013
981
 
1014
982
  private createArbitraryOptionDefault(input: string, _options: any[]): any {
@@ -27,6 +27,7 @@ import {
27
27
  ContactGroupsEvent,
28
28
  ContactHistoryPage,
29
29
  ContactLanguageChangedEvent,
30
+ ContactStatusChangedEvent,
30
31
  MsgEvent,
31
32
  NameChangedEvent,
32
33
  OptInEvent,
@@ -61,6 +62,7 @@ export enum Events {
61
62
  CONTACT_GROUPS_CHANGED = 'contact_groups_changed',
62
63
  CONTACT_LANGUAGE_CHANGED = 'contact_language_changed',
63
64
  CONTACT_NAME_CHANGED = 'contact_name_changed',
65
+ CONTACT_STATUS_CHANGED = 'contact_status_changed',
64
66
  CONTACT_URNS_CHANGED = 'contact_urns_changed',
65
67
  IVR_CREATED = 'ivr_created',
66
68
  MSG_CREATED = 'msg_created',
@@ -97,28 +99,10 @@ const renderInfoList = (singular: string, plural: string, items: any[]) => {
97
99
  };
98
100
 
99
101
  const renderChannelEvent = (event: ChannelEvent): string => {
100
- if (event.event.type === 'mt_miss') {
101
- return 'Missed outgoing call';
102
- } else if (event.event.type === 'mo_miss') {
103
- return 'Missed incoming call';
104
- } else if (event.event.type === 'new_conversation') {
105
- return 'Started conversation';
106
- } else if (event.channel_event_type === 'welcome_message') {
107
- return 'Welcome Message Sent';
108
- } else if (event.event.type === 'referral') {
109
- return 'Referred';
110
- } else if (event.event.type === 'follow') {
111
- return 'Followed';
102
+ if (event.channel_event_type === 'welcome_message') {
103
+ return 'Welcome message sent';
112
104
  } else if (event.event.type === 'stop_contact') {
113
105
  return 'Stopped';
114
- } else if (event.event.type === 'mt_call') {
115
- return 'Outgoing Phone Call'; // deprecated
116
- } else if (event.event.type == 'mo_call') {
117
- return 'Incoming Phone call'; // deprecated
118
- } else if (event.event.type == 'optin') {
119
- return `Opted in to **${event.event.optin?.name}**`; // deprecated
120
- } else if (event.event.type == 'optout') {
121
- return `Opted out of **${event.event.optin?.name}**`; // deprecated
122
106
  }
123
107
  };
124
108
 
@@ -220,6 +204,12 @@ export const renderContactLanguageChangedEvent = (
220
204
  return `Language updated to **${event.language}**`;
221
205
  };
222
206
 
207
+ export const renderContactStatusChangedEvent = (
208
+ event: ContactStatusChangedEvent
209
+ ): string => {
210
+ return `Status updated to **${event.status}**`;
211
+ };
212
+
223
213
  export const renderCallEvent = (event: CallEvent): string => {
224
214
  if (event.type === Events.CALL_CREATED) {
225
215
  return `Call started`;
@@ -232,7 +222,7 @@ export const renderCallEvent = (event: CallEvent): string => {
232
222
 
233
223
  export const renderOptInEvent = (event: OptInEvent): string => {
234
224
  if (event.type === Events.OPTIN_REQUESTED) {
235
- return `Requested opt-in for ${event.optin.name}`;
225
+ return `Requested opt-in for **${event.optin.name}**`;
236
226
  } else if (event.type === Events.OPTIN_STARTED) {
237
227
  return `Opted in to **${event.optin.name}**`;
238
228
  } else if (event.type === Events.OPTIN_STOPPED) {
@@ -632,53 +622,58 @@ export class ContactChat extends ContactStoreElement {
632
622
  public getEventMessage(event: ContactEvent): ChatEvent {
633
623
  let message = null;
634
624
  switch (event.type) {
635
- case Events.TICKET_OPENED:
625
+ case Events.AIRTIME_TRANSFERRED:
636
626
  message = {
637
627
  type: MessageType.Inline,
638
- text: renderTicketAction(event as TicketEvent, 'opened')
628
+ text: renderAirtimeTransferredEvent(event as AirtimeTransferredEvent)
639
629
  };
640
630
  break;
641
- case Events.TICKET_ASSIGNED:
631
+ case Events.CALL_CREATED:
632
+ case Events.CALL_MISSED:
633
+ case Events.CALL_RECEIVED:
642
634
  message = {
643
635
  type: MessageType.Inline,
644
- text: renderTicketAssigned(event as TicketEvent)
636
+ text: renderCallEvent(event as CallEvent)
645
637
  };
646
638
  break;
647
- case Events.TICKET_REOPENED:
639
+ case Events.CHAT_STARTED:
648
640
  message = {
649
641
  type: MessageType.Inline,
650
- text: renderTicketAction(event as TicketEvent, 'reopened')
642
+ text: renderChatStartedEvent(event as ChatStartedEvent)
651
643
  };
652
644
  break;
653
- case Events.TICKET_CLOSED:
645
+ case Events.CONTACT_FIELD_CHANGED:
654
646
  message = {
655
647
  type: MessageType.Inline,
656
- text: renderTicketAction(event as TicketEvent, 'closed')
648
+ text: renderUpdateEvent(event as UpdateFieldEvent)
657
649
  };
658
650
  break;
659
- case Events.TICKET_TOPIC_CHANGED:
651
+ case Events.CONTACT_GROUPS_CHANGED:
660
652
  message = {
661
653
  type: MessageType.Inline,
662
- text: `Topic changed to **${(event as TicketEvent).topic.name}**`
654
+ text: renderContactGroupsEvent(event as ContactGroupsEvent)
663
655
  };
664
656
  break;
665
- case Events.RUN_STARTED:
666
- case Events.RUN_ENDED:
657
+ case Events.CONTACT_LANGUAGE_CHANGED:
667
658
  message = {
668
659
  type: MessageType.Inline,
669
- text: renderRunEvent(event as RunEvent)
660
+ text: renderContactLanguageChangedEvent(
661
+ event as ContactLanguageChangedEvent
662
+ )
670
663
  };
671
664
  break;
672
- case Events.CONTACT_FIELD_CHANGED:
665
+ case Events.CONTACT_NAME_CHANGED:
673
666
  message = {
674
667
  type: MessageType.Inline,
675
- text: renderUpdateEvent(event as UpdateFieldEvent)
668
+ text: renderNameChanged(event as NameChangedEvent)
676
669
  };
677
670
  break;
678
- case Events.CONTACT_NAME_CHANGED:
671
+ case Events.CONTACT_STATUS_CHANGED:
679
672
  message = {
680
673
  type: MessageType.Inline,
681
- text: renderNameChanged(event as NameChangedEvent)
674
+ text: renderContactStatusChangedEvent(
675
+ event as ContactStatusChangedEvent
676
+ )
682
677
  };
683
678
  break;
684
679
  case Events.CONTACT_URNS_CHANGED:
@@ -687,52 +682,55 @@ export class ContactChat extends ContactStoreElement {
687
682
  text: renderContactURNsChanged(event as URNsChangedEvent)
688
683
  };
689
684
  break;
690
- case Events.CONTACT_GROUPS_CHANGED:
685
+ case Events.OPTIN_REQUESTED:
686
+ case Events.OPTIN_STARTED:
687
+ case Events.OPTIN_STOPPED:
691
688
  message = {
692
689
  type: MessageType.Inline,
693
- text: renderContactGroupsEvent(event as ContactGroupsEvent)
690
+ text: renderOptInEvent(event as OptInEvent)
694
691
  };
695
692
  break;
696
- case Events.AIRTIME_TRANSFERRED:
693
+ case Events.RUN_STARTED:
694
+ case Events.RUN_ENDED:
697
695
  message = {
698
696
  type: MessageType.Inline,
699
- text: renderAirtimeTransferredEvent(event as AirtimeTransferredEvent)
697
+ text: renderRunEvent(event as RunEvent)
700
698
  };
701
699
  break;
702
- case Events.CALL_CREATED:
703
- case Events.CALL_MISSED:
704
- case Events.CALL_RECEIVED:
700
+ case Events.TICKET_ASSIGNED:
705
701
  message = {
706
702
  type: MessageType.Inline,
707
- text: renderCallEvent(event as CallEvent)
703
+ text: renderTicketAssigned(event as TicketEvent)
708
704
  };
709
705
  break;
710
- case Events.CHANNEL_EVENT:
706
+ case Events.TICKET_CLOSED:
711
707
  message = {
712
708
  type: MessageType.Inline,
713
- text: renderChannelEvent(event as ChannelEvent)
709
+ text: renderTicketAction(event as TicketEvent, 'closed')
714
710
  };
715
711
  break;
716
- case Events.CHAT_STARTED:
712
+ case Events.TICKET_OPENED:
717
713
  message = {
718
714
  type: MessageType.Inline,
719
- text: renderChatStartedEvent(event as ChatStartedEvent)
715
+ text: renderTicketAction(event as TicketEvent, 'opened')
720
716
  };
721
717
  break;
722
- case Events.CONTACT_LANGUAGE_CHANGED:
718
+ case Events.TICKET_REOPENED:
723
719
  message = {
724
720
  type: MessageType.Inline,
725
- text: renderContactLanguageChangedEvent(
726
- event as ContactLanguageChangedEvent
727
- )
721
+ text: renderTicketAction(event as TicketEvent, 'reopened')
728
722
  };
729
723
  break;
730
- case Events.OPTIN_REQUESTED:
731
- case Events.OPTIN_STARTED:
732
- case Events.OPTIN_STOPPED:
724
+ case Events.TICKET_TOPIC_CHANGED:
733
725
  message = {
734
726
  type: MessageType.Inline,
735
- text: renderOptInEvent(event as OptInEvent)
727
+ text: `Topic changed to **${(event as TicketEvent).topic.name}**`
728
+ };
729
+ break;
730
+ case Events.CHANNEL_EVENT: // deprecated
731
+ message = {
732
+ type: MessageType.Inline,
733
+ text: renderChannelEvent(event as ChannelEvent)
736
734
  };
737
735
  break;
738
736
  }
@@ -68,6 +68,11 @@ export interface NamedObject {
68
68
  name: string;
69
69
  }
70
70
 
71
+ export interface FieldReference {
72
+ key: string;
73
+ name: string;
74
+ }
75
+
71
76
  export interface Group extends NamedObject {
72
77
  status?: string;
73
78
  system?: boolean;
@@ -111,7 +116,7 @@ export interface RemoveFromGroup extends Action {
111
116
  }
112
117
 
113
118
  export interface SetContactField extends Action {
114
- field: NamedObject;
119
+ field: FieldReference;
115
120
  value: string;
116
121
  }
117
122
 
@@ -175,7 +180,8 @@ export interface CallLLM extends Action {
175
180
  llm: NamedObject;
176
181
  instructions: string;
177
182
  input: string;
178
- result_name: string;
183
+ result_name?: string;
184
+ output_local?: string;
179
185
  }
180
186
 
181
187
  export interface OpenTicket extends Action {