@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,18 +1,119 @@
1
- import { open_ticket } from '../actions/open_ticket';
1
+ import { COLORS } from '../types';
2
+ import { generateUUID, createSuccessFailureRouter } from '../../utils';
3
+ import { html } from 'lit';
2
4
  export const split_by_ticket = {
3
5
  type: 'split_by_ticket',
4
- action: open_ticket,
5
- router: {
6
- type: 'switch',
7
- defaultCategory: 'Failure',
8
- operand: '@locals._new_ticket',
9
- rules: [
10
- {
11
- type: 'has_text',
12
- arguments: [],
13
- categoryName: 'Success'
14
- }
15
- ]
6
+ name: 'Open Ticket',
7
+ color: COLORS.create,
8
+ form: {
9
+ topic: {
10
+ type: 'select',
11
+ label: 'Topic',
12
+ required: true,
13
+ placeholder: 'Select a topic',
14
+ options: [],
15
+ endpoint: '/api/v2/topics.json',
16
+ valueKey: 'uuid',
17
+ nameKey: 'name',
18
+ maxWidth: '200px'
19
+ },
20
+ assignee: {
21
+ type: 'select',
22
+ label: 'Assignee',
23
+ required: false,
24
+ placeholder: 'Select an agent (optional)',
25
+ endpoint: '/api/v2/users.json',
26
+ valueKey: 'uuid',
27
+ getName: (item) => {
28
+ return item.name || [item.first_name, item.last_name].join(' ');
29
+ },
30
+ clearable: true
31
+ },
32
+ note: {
33
+ type: 'textarea',
34
+ label: 'Note',
35
+ required: false,
36
+ placeholder: 'Enter a note for the ticket (optional)',
37
+ minHeight: 100
38
+ }
39
+ },
40
+ layout: [{ type: 'row', items: ['topic', 'assignee'] }, 'note'],
41
+ render: (node) => {
42
+ var _a, _b;
43
+ const openTicketAction = (_a = node.actions) === null || _a === void 0 ? void 0 : _a.find((action) => action.type === 'open_ticket');
44
+ return html `
45
+ <div class="body">
46
+ ${((_b = openTicketAction === null || openTicketAction === void 0 ? void 0 : openTicketAction.topic) === null || _b === void 0 ? void 0 : _b.name) || 'Configure ticket'}
47
+ </div>
48
+ `;
49
+ },
50
+ toFormData: (node) => {
51
+ var _a;
52
+ // Extract data from the existing node structure
53
+ const openTicketAction = (_a = node.actions) === null || _a === void 0 ? void 0 : _a.find((action) => action.type === 'open_ticket');
54
+ return {
55
+ uuid: node.uuid,
56
+ topic: (openTicketAction === null || openTicketAction === void 0 ? void 0 : openTicketAction.topic)
57
+ ? [
58
+ {
59
+ uuid: openTicketAction.topic.uuid,
60
+ name: openTicketAction.topic.name
61
+ }
62
+ ]
63
+ : [],
64
+ assignee: (openTicketAction === null || openTicketAction === void 0 ? void 0 : openTicketAction.assignee)
65
+ ? [
66
+ {
67
+ uuid: openTicketAction.assignee.uuid,
68
+ name: openTicketAction.assignee.name
69
+ }
70
+ ]
71
+ : [],
72
+ note: (openTicketAction === null || openTicketAction === void 0 ? void 0 : openTicketAction.note) || ''
73
+ };
74
+ },
75
+ fromFormData: (formData, originalNode) => {
76
+ var _a, _b, _c;
77
+ // Find existing open_ticket action to preserve its UUID
78
+ const existingOpenTicketAction = (_a = originalNode.actions) === null || _a === void 0 ? void 0 : _a.find((action) => action.type === 'open_ticket');
79
+ const openTicketUuid = (existingOpenTicketAction === null || existingOpenTicketAction === void 0 ? void 0 : existingOpenTicketAction.uuid) || generateUUID();
80
+ // Create open_ticket action
81
+ const openTicketAction = {
82
+ type: 'open_ticket',
83
+ uuid: openTicketUuid,
84
+ topic: formData.topic && formData.topic.length > 0
85
+ ? {
86
+ uuid: formData.topic[0].uuid,
87
+ name: formData.topic[0].name
88
+ }
89
+ : undefined,
90
+ assignee: formData.assignee && formData.assignee.length > 0
91
+ ? {
92
+ uuid: formData.assignee[0].uuid,
93
+ name: formData.assignee[0].name ||
94
+ [
95
+ formData.assignee[0].first_name,
96
+ formData.assignee[0].last_name
97
+ ].join(' ')
98
+ }
99
+ : undefined,
100
+ note: formData.note || ''
101
+ };
102
+ // Create categories and exits for Success and Failure
103
+ const existingCategories = ((_b = originalNode.router) === null || _b === void 0 ? void 0 : _b.categories) || [];
104
+ const existingExits = originalNode.exits || [];
105
+ const existingCases = ((_c = originalNode.router) === null || _c === void 0 ? void 0 : _c.cases) || [];
106
+ const { router, exits } = createSuccessFailureRouter('@locals._new_ticket', {
107
+ type: 'has_text',
108
+ arguments: []
109
+ }, existingCategories, existingExits, existingCases);
110
+ // Return the complete node
111
+ return {
112
+ uuid: originalNode.uuid,
113
+ actions: [openTicketAction],
114
+ router: router,
115
+ exits: exits
116
+ };
16
117
  }
17
118
  };
18
119
  //# sourceMappingURL=split_by_ticket.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"split_by_ticket.js","sourceRoot":"","sources":["../../../../src/flow/nodes/split_by_ticket.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAGrD,MAAM,CAAC,MAAM,eAAe,GAAe;IACzC,IAAI,EAAE,iBAAiB;IACvB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ;QACd,eAAe,EAAE,SAAS;QAC1B,OAAO,EAAE,qBAAqB;QAC9B,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,UAAU;gBAChB,SAAS,EAAE,EAAE;gBACb,YAAY,EAAE,SAAS;aACxB;SACF;KACF;CACF,CAAC","sourcesContent":["import { open_ticket } from '../actions/open_ticket';\nimport { NodeConfig } from '../types';\n\nexport const split_by_ticket: NodeConfig = {\n type: 'split_by_ticket',\n action: open_ticket,\n router: {\n type: 'switch',\n defaultCategory: 'Failure',\n operand: '@locals._new_ticket',\n rules: [\n {\n type: 'has_text',\n arguments: [],\n categoryName: 'Success'\n }\n ]\n }\n};\n"]}
1
+ {"version":3,"file":"split_by_ticket.js","sourceRoot":"","sources":["../../../../src/flow/nodes/split_by_ticket.ts"],"names":[],"mappings":"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","sourcesContent":["import { COLORS, NodeConfig } from '../types';\nimport { Node, OpenTicket } from '../../store/flow-definition';\nimport { generateUUID, createSuccessFailureRouter } from '../../utils';\nimport { html } from 'lit';\n\nexport const split_by_ticket: NodeConfig = {\n type: 'split_by_ticket',\n name: 'Open Ticket',\n color: COLORS.create,\n form: {\n topic: {\n type: 'select',\n label: 'Topic',\n required: true,\n placeholder: 'Select a topic',\n options: [],\n endpoint: '/api/v2/topics.json',\n valueKey: 'uuid',\n nameKey: 'name',\n maxWidth: '200px'\n },\n assignee: {\n type: 'select',\n label: 'Assignee',\n required: false,\n placeholder: 'Select an agent (optional)',\n endpoint: '/api/v2/users.json',\n valueKey: 'uuid',\n getName: (item: {\n first_name?: string;\n last_name?: string;\n name?: string;\n }) => {\n return item.name || [item.first_name, item.last_name].join(' ');\n },\n clearable: true\n },\n note: {\n type: 'textarea',\n label: 'Note',\n required: false,\n placeholder: 'Enter a note for the ticket (optional)',\n minHeight: 100\n }\n },\n layout: [{ type: 'row', items: ['topic', 'assignee'] }, 'note'],\n render: (node: Node) => {\n const openTicketAction = node.actions?.find(\n (action) => action.type === 'open_ticket'\n ) as OpenTicket;\n return html`\n <div class=\"body\">\n ${openTicketAction?.topic?.name || 'Configure ticket'}\n </div>\n `;\n },\n toFormData: (node: Node) => {\n // Extract data from the existing node structure\n const openTicketAction = node.actions?.find(\n (action) => action.type === 'open_ticket'\n ) as any;\n\n return {\n uuid: node.uuid,\n topic: openTicketAction?.topic\n ? [\n {\n uuid: openTicketAction.topic.uuid,\n name: openTicketAction.topic.name\n }\n ]\n : [],\n assignee: openTicketAction?.assignee\n ? [\n {\n uuid: openTicketAction.assignee.uuid,\n name: openTicketAction.assignee.name\n }\n ]\n : [],\n note: openTicketAction?.note || ''\n };\n },\n fromFormData: (formData: any, originalNode: Node): Node => {\n // Find existing open_ticket action to preserve its UUID\n const existingOpenTicketAction = originalNode.actions?.find(\n (action) => action.type === 'open_ticket'\n );\n const openTicketUuid = existingOpenTicketAction?.uuid || generateUUID();\n\n // Create open_ticket action\n const openTicketAction: OpenTicket = {\n type: 'open_ticket',\n uuid: openTicketUuid,\n topic:\n formData.topic && formData.topic.length > 0\n ? {\n uuid: formData.topic[0].uuid,\n name: formData.topic[0].name\n }\n : undefined,\n assignee:\n formData.assignee && formData.assignee.length > 0\n ? {\n uuid: formData.assignee[0].uuid,\n name:\n formData.assignee[0].name ||\n [\n formData.assignee[0].first_name,\n formData.assignee[0].last_name\n ].join(' ')\n }\n : undefined,\n note: formData.note || ''\n };\n\n // Create categories and exits for Success and Failure\n const existingCategories = originalNode.router?.categories || [];\n const existingExits = originalNode.exits || [];\n const existingCases = originalNode.router?.cases || [];\n\n const { router, exits } = createSuccessFailureRouter(\n '@locals._new_ticket',\n {\n type: 'has_text',\n arguments: []\n },\n existingCategories,\n existingExits,\n existingCases\n );\n\n // Return the complete node\n return {\n uuid: originalNode.uuid,\n actions: [openTicketAction],\n router: router,\n exits: exits\n };\n }\n};\n"]}
@@ -1,18 +1,164 @@
1
- import { call_webhook } from '../actions/call_webhook';
1
+ import { COLORS } from '../types';
2
+ import { generateUUID, createSuccessFailureRouter } from '../../utils';
3
+ import { html } from 'lit';
4
+ const defaultPost = `@(json(object(
5
+ "contact", object(
6
+ "uuid", contact.uuid,
7
+ "name", contact.name,
8
+ "urn", contact.urn
9
+ ),
10
+ "flow", object(
11
+ "uuid", run.flow.uuid,
12
+ "name", run.flow.name
13
+ ),
14
+ "results", foreach_value(results, extract_object, "value", "category")
15
+ )))`;
2
16
  export const split_by_webhook = {
3
17
  type: 'split_by_webhook',
4
- action: call_webhook,
5
- router: {
6
- type: 'switch',
7
- defaultCategory: 'Failure',
8
- operand: '@webhook.status',
9
- rules: [
10
- {
11
- type: 'has_number_between',
12
- arguments: ['200', '299'],
13
- categoryName: 'Success'
18
+ name: 'Call Webhook',
19
+ color: COLORS.call,
20
+ form: {
21
+ method: {
22
+ type: 'select',
23
+ required: true,
24
+ options: ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'PATCH'],
25
+ maxWidth: '120px',
26
+ searchable: false
27
+ },
28
+ url: {
29
+ type: 'text',
30
+ required: true,
31
+ evaluated: true,
32
+ placeholder: 'https://example.com/webhook'
33
+ },
34
+ headers: {
35
+ type: 'key-value',
36
+ sortable: true,
37
+ keyPlaceholder: 'Header name',
38
+ valuePlaceholder: 'Header value',
39
+ minRows: 0
40
+ },
41
+ body: {
42
+ type: 'textarea',
43
+ evaluated: true,
44
+ placeholder: 'Request body content (JSON, XML, etc.)',
45
+ minHeight: 200,
46
+ dependsOn: ['method'],
47
+ computeValue: (values, currentValue, originalValues) => {
48
+ // Check if method is POST (handle both string and select object formats)
49
+ const method = Array.isArray(values.method) && values.method.length > 0
50
+ ? values.method[0].value || values.method[0].name
51
+ : values.method;
52
+ if (method === 'POST') {
53
+ // For POST, provide the template if body is empty or was never set by user
54
+ if (!currentValue || currentValue.trim() === '') {
55
+ return defaultPost;
56
+ }
57
+ }
58
+ else {
59
+ // For non-POST methods, clear the body if it was auto-generated or empty
60
+ // Check if the original body was empty (user never specified a body)
61
+ const originalBody = (originalValues === null || originalValues === void 0 ? void 0 : originalValues.body) || '';
62
+ const isOriginallyEmpty = !originalBody || originalBody.trim() === '';
63
+ // Clear if: originally empty, contains default template, or is currently empty
64
+ if (isOriginallyEmpty ||
65
+ !currentValue ||
66
+ currentValue.trim() === '' ||
67
+ currentValue.trim() === defaultPost.trim()) {
68
+ return '';
69
+ }
70
+ }
71
+ return currentValue; // Keep existing value if user has customized it
14
72
  }
15
- ]
73
+ }
74
+ },
75
+ layout: [
76
+ // Row with method and URL side by side
77
+ { type: 'row', items: ['method', 'url'] },
78
+ // Advanced group with nested layouts
79
+ {
80
+ type: 'group',
81
+ label: 'Headers',
82
+ items: ['headers'],
83
+ collapsible: true,
84
+ collapsed: true,
85
+ helpText: 'Configure authentication or custom headers',
86
+ getGroupValueCount: (formData) => {
87
+ var _a;
88
+ return ((_a = formData.headers) === null || _a === void 0 ? void 0 : _a.length) || 0;
89
+ }
90
+ },
91
+ {
92
+ type: 'group',
93
+ label: 'Body',
94
+ items: ['body'],
95
+ collapsible: true,
96
+ collapsed: true,
97
+ helpText: 'Configure the request payload',
98
+ getGroupValueCount: (formData) => {
99
+ return !!(formData.body &&
100
+ formData.body.trim() !== '' &&
101
+ formData.body !== defaultPost);
102
+ }
103
+ }
104
+ ],
105
+ render: (node) => {
106
+ var _a;
107
+ const callWebhookAction = (_a = node.actions) === null || _a === void 0 ? void 0 : _a.find((action) => action.type === 'call_webhook');
108
+ return html `
109
+ <div
110
+ class="body"
111
+ style="word-wrap: break-word; overflow-wrap: break-word; hyphens: auto;"
112
+ >
113
+ ${(callWebhookAction === null || callWebhookAction === void 0 ? void 0 : callWebhookAction.url) || 'Configure webhook'}
114
+ </div>
115
+ `;
116
+ },
117
+ toFormData: (node) => {
118
+ var _a;
119
+ // Extract data from the existing node structure
120
+ const callWebhookAction = (_a = node.actions) === null || _a === void 0 ? void 0 : _a.find((action) => action.type === 'call_webhook');
121
+ return {
122
+ uuid: node.uuid,
123
+ method: (callWebhookAction === null || callWebhookAction === void 0 ? void 0 : callWebhookAction.method) || 'GET',
124
+ url: (callWebhookAction === null || callWebhookAction === void 0 ? void 0 : callWebhookAction.url) || '',
125
+ headers: (callWebhookAction === null || callWebhookAction === void 0 ? void 0 : callWebhookAction.headers) || [],
126
+ body: (callWebhookAction === null || callWebhookAction === void 0 ? void 0 : callWebhookAction.body) || ''
127
+ };
128
+ },
129
+ fromFormData: (formData, originalNode) => {
130
+ var _a, _b, _c;
131
+ // Get method selection
132
+ const methodSelection = Array.isArray(formData.method) && formData.method.length > 0
133
+ ? formData.method[0]
134
+ : { value: 'GET', name: 'GET' };
135
+ // Find existing call_webhook action to preserve its UUID
136
+ const existingCallWebhookAction = (_a = originalNode.actions) === null || _a === void 0 ? void 0 : _a.find((action) => action.type === 'call_webhook');
137
+ const callWebhookUuid = (existingCallWebhookAction === null || existingCallWebhookAction === void 0 ? void 0 : existingCallWebhookAction.uuid) || generateUUID();
138
+ // Create call_webhook action
139
+ const callWebhookAction = {
140
+ type: 'call_webhook',
141
+ uuid: callWebhookUuid,
142
+ method: methodSelection.value,
143
+ url: formData.url || '',
144
+ headers: formData.headers || [],
145
+ body: formData.body || ''
146
+ };
147
+ // Create categories and exits for Success and Failure
148
+ const existingCategories = ((_b = originalNode.router) === null || _b === void 0 ? void 0 : _b.categories) || [];
149
+ const existingExits = originalNode.exits || [];
150
+ const existingCases = ((_c = originalNode.router) === null || _c === void 0 ? void 0 : _c.cases) || [];
151
+ const { router, exits } = createSuccessFailureRouter('@webhook.status', {
152
+ type: 'has_number_between',
153
+ arguments: ['200', '299']
154
+ }, existingCategories, existingExits, existingCases);
155
+ // Return the complete node
156
+ return {
157
+ uuid: originalNode.uuid,
158
+ actions: [callWebhookAction],
159
+ router: router,
160
+ exits: exits
161
+ };
16
162
  }
17
163
  };
18
164
  //# sourceMappingURL=split_by_webhook.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"split_by_webhook.js","sourceRoot":"","sources":["../../../../src/flow/nodes/split_by_webhook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAGvD,MAAM,CAAC,MAAM,gBAAgB,GAAe;IAC1C,IAAI,EAAE,kBAAkB;IACxB,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ;QACd,eAAe,EAAE,SAAS;QAC1B,OAAO,EAAE,iBAAiB;QAC1B,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,oBAAoB;gBAC1B,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC;gBACzB,YAAY,EAAE,SAAS;aACxB;SACF;KACF;CACF,CAAC","sourcesContent":["import { call_webhook } from '../actions/call_webhook';\nimport { NodeConfig } from '../types';\n\nexport const split_by_webhook: NodeConfig = {\n type: 'split_by_webhook',\n action: call_webhook,\n router: {\n type: 'switch',\n defaultCategory: 'Failure',\n operand: '@webhook.status',\n rules: [\n {\n type: 'has_number_between',\n arguments: ['200', '299'],\n categoryName: 'Success'\n }\n ]\n }\n};\n"]}
1
+ {"version":3,"file":"split_by_webhook.js","sourceRoot":"","sources":["../../../../src/flow/nodes/split_by_webhook.ts"],"names":[],"mappings":"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","sourcesContent":["import { COLORS, NodeConfig } from '../types';\nimport { CallWebhook, Node } from '../../store/flow-definition';\nimport { generateUUID, createSuccessFailureRouter } from '../../utils';\nimport { html } from 'lit';\n\nconst defaultPost = `@(json(object(\n \"contact\", object(\n \"uuid\", contact.uuid, \n \"name\", contact.name, \n \"urn\", contact.urn\n ),\n \"flow\", object(\n \"uuid\", run.flow.uuid, \n \"name\", run.flow.name\n ),\n \"results\", foreach_value(results, extract_object, \"value\", \"category\")\n)))`;\n\nexport const split_by_webhook: NodeConfig = {\n type: 'split_by_webhook',\n name: 'Call Webhook',\n color: COLORS.call,\n form: {\n method: {\n type: 'select',\n required: true,\n options: ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'PATCH'],\n maxWidth: '120px',\n searchable: false\n },\n url: {\n type: 'text',\n required: true,\n evaluated: true,\n placeholder: 'https://example.com/webhook'\n },\n headers: {\n type: 'key-value',\n sortable: true,\n keyPlaceholder: 'Header name',\n valuePlaceholder: 'Header value',\n minRows: 0\n },\n body: {\n type: 'textarea',\n evaluated: true,\n placeholder: 'Request body content (JSON, XML, etc.)',\n minHeight: 200,\n dependsOn: ['method'],\n computeValue: (\n values: Record<string, any>,\n currentValue: any,\n originalValues?: Record<string, any>\n ) => {\n // Check if method is POST (handle both string and select object formats)\n const method =\n Array.isArray(values.method) && values.method.length > 0\n ? values.method[0].value || values.method[0].name\n : values.method;\n\n if (method === 'POST') {\n // For POST, provide the template if body is empty or was never set by user\n if (!currentValue || currentValue.trim() === '') {\n return defaultPost;\n }\n } else {\n // For non-POST methods, clear the body if it was auto-generated or empty\n // Check if the original body was empty (user never specified a body)\n const originalBody = originalValues?.body || '';\n const isOriginallyEmpty = !originalBody || originalBody.trim() === '';\n\n // Clear if: originally empty, contains default template, or is currently empty\n if (\n isOriginallyEmpty ||\n !currentValue ||\n currentValue.trim() === '' ||\n currentValue.trim() === defaultPost.trim()\n ) {\n return '';\n }\n }\n\n return currentValue; // Keep existing value if user has customized it\n }\n }\n },\n layout: [\n // Row with method and URL side by side\n { type: 'row', items: ['method', 'url'] },\n // Advanced group with nested layouts\n {\n type: 'group',\n label: 'Headers',\n items: ['headers'],\n collapsible: true,\n collapsed: true,\n helpText: 'Configure authentication or custom headers',\n getGroupValueCount: (formData: any) => {\n return formData.headers?.length || 0;\n }\n },\n {\n type: 'group',\n label: 'Body',\n items: ['body'],\n collapsible: true,\n collapsed: true,\n helpText: 'Configure the request payload',\n getGroupValueCount: (formData: any) => {\n return !!(\n formData.body &&\n formData.body.trim() !== '' &&\n formData.body !== defaultPost\n );\n }\n }\n ],\n render: (node: Node) => {\n const callWebhookAction = node.actions?.find(\n (action) => action.type === 'call_webhook'\n ) as CallWebhook;\n return html`\n <div\n class=\"body\"\n style=\"word-wrap: break-word; overflow-wrap: break-word; hyphens: auto;\"\n >\n ${callWebhookAction?.url || 'Configure webhook'}\n </div>\n `;\n },\n toFormData: (node: Node) => {\n // Extract data from the existing node structure\n const callWebhookAction = node.actions?.find(\n (action) => action.type === 'call_webhook'\n ) as any;\n\n return {\n uuid: node.uuid,\n method: callWebhookAction?.method || 'GET',\n url: callWebhookAction?.url || '',\n headers: callWebhookAction?.headers || [],\n body: callWebhookAction?.body || ''\n };\n },\n fromFormData: (formData: any, originalNode: Node): Node => {\n // Get method selection\n const methodSelection =\n Array.isArray(formData.method) && formData.method.length > 0\n ? formData.method[0]\n : { value: 'GET', name: 'GET' };\n\n // Find existing call_webhook action to preserve its UUID\n const existingCallWebhookAction = originalNode.actions?.find(\n (action) => action.type === 'call_webhook'\n );\n const callWebhookUuid = existingCallWebhookAction?.uuid || generateUUID();\n\n // Create call_webhook action\n const callWebhookAction: CallWebhook = {\n type: 'call_webhook',\n uuid: callWebhookUuid,\n method: methodSelection.value,\n url: formData.url || '',\n headers: formData.headers || [],\n body: formData.body || ''\n };\n\n // Create categories and exits for Success and Failure\n const existingCategories = originalNode.router?.categories || [];\n const existingExits = originalNode.exits || [];\n const existingCases = originalNode.router?.cases || [];\n\n const { router, exits } = createSuccessFailureRouter(\n '@webhook.status',\n {\n type: 'has_number_between',\n arguments: ['200', '299']\n },\n existingCategories,\n existingExits,\n existingCases\n );\n\n // Return the complete node\n return {\n uuid: originalNode.uuid,\n actions: [callWebhookAction],\n router: router,\n exits: exits\n };\n }\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/flow/types.ts"],"names":[],"mappings":"AAiQA,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,SAAS;IACjB,SAAS,EAAE,SAAS;IACpB,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,SAAS;IACjB,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,SAAS;IACf,GAAG,EAAE,SAAS;IACd,MAAM,EAAE,SAAS;CAClB,CAAC","sourcesContent":["import { TemplateResult } from 'lit-html';\nimport { Action, Node } from '../store/flow-definition';\n\nexport interface ValidationResult {\n valid: boolean;\n errors: { [key: string]: string };\n}\n\n// Component attribute interfaces - these define what's allowed for each component type\nexport interface TextInputAttributes {\n type?: 'text' | 'email' | 'number' | 'url' | 'tel';\n placeholder?: string;\n clearable?: boolean;\n maxlength?: number;\n gsm?: boolean;\n autogrow?: boolean;\n textarea?: boolean;\n submitOnEnter?: boolean;\n}\n\nexport interface CompletionAttributes {\n placeholder?: string;\n clearable?: boolean;\n maxlength?: number;\n gsm?: boolean;\n autogrow?: boolean;\n textarea?: boolean;\n expressions?: string;\n counter?: string;\n minHeight?: number;\n}\n\nexport interface SelectAttributes {\n placeholder?: string;\n multi?: boolean;\n searchable?: boolean;\n tags?: boolean;\n emails?: boolean;\n clearable?: boolean;\n endpoint?: string;\n valueKey?: string;\n nameKey?: string;\n queryParam?: string;\n maxItems?: number;\n maxItemsText?: string;\n expressions?: string;\n options?: Array<{ name: string; value: any }>;\n sorted?: boolean;\n allowCreate?: boolean;\n jsonValue?: boolean;\n spaceSelect?: boolean;\n infoText?: string;\n}\n\nexport interface CheckboxAttributes {\n label?: string;\n size?: number;\n disabled?: boolean;\n animateChange?: string;\n}\n\nexport interface SliderAttributes {\n min?: number;\n max?: number;\n range?: boolean;\n}\n\nexport interface FormData extends Record<string, any> {}\n\nexport interface FormConfig {\n form?: Record<string, FieldConfig>;\n layout?: LayoutItem[];\n sanitize?: (formData: FormData) => void;\n validate?: (formData: FormData) => ValidationResult;\n}\n\nexport interface NodeConfig extends FormConfig {\n type: string;\n name?: string;\n color?: string;\n action?: ActionConfig;\n router?: {\n type: 'switch' | 'random';\n defaultCategory?: string;\n operand?: string;\n configurable?: boolean; // can the rules be configured in the UI\n rules?: {\n type:\n | 'has_number_between'\n | 'has_string'\n | 'has_value'\n | 'has_not_value'\n | 'has_text';\n arguments: string[];\n categoryName: string;\n }[];\n };\n\n toFormData?: (node: Node) => FormData;\n fromFormData?: (formData: FormData, originalNode: Node) => Node;\n render?: (node: Node) => TemplateResult;\n}\n\n// New field configuration system for generic form generation\nexport interface BaseFieldConfig {\n label?: string;\n required?: boolean;\n evaluated?: boolean;\n dependsOn?: string[];\n computeValue?: (\n values: Record<string, any>,\n currentValue: any,\n originalValues?: Record<string, any>\n ) => any;\n\n // Validation properties\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n helpText?: string;\n\n // Layout properties\n maxWidth?: string;\n\n // Conditional rendering\n conditions?: {\n visible?: (formData: Record<string, any>) => boolean;\n disabled?: (formData: Record<string, any>) => boolean;\n };\n}\n\nexport interface TextFieldConfig extends BaseFieldConfig {\n type: 'text';\n placeholder?: string;\n}\n\nexport interface TextareaFieldConfig extends BaseFieldConfig {\n type: 'textarea';\n placeholder?: string;\n rows?: number;\n minHeight?: number;\n}\n\nexport interface SelectFieldConfig extends BaseFieldConfig {\n type: 'select';\n options?: string[] | { value: string; label: string }[];\n multi?: boolean;\n clearable?: boolean;\n searchable?: boolean;\n tags?: boolean;\n placeholder?: string;\n maxItems?: number;\n valueKey?: string;\n nameKey?: string;\n endpoint?: string;\n emails?: boolean;\n getName?: (item: any) => string;\n flavor?: 'small' | 'large';\n createArbitraryOption?: (input: string, options: any[]) => any;\n allowCreate?: boolean;\n}\n\nexport interface KeyValueFieldConfig extends BaseFieldConfig {\n type: 'key-value';\n sortable?: boolean;\n keyPlaceholder?: string;\n valuePlaceholder?: string;\n minRows?: number;\n}\n\nexport interface ArrayFieldConfig extends BaseFieldConfig {\n type: 'array';\n itemConfig: Record<string, FieldConfig>;\n sortable?: boolean;\n minItems?: number;\n maxItems?: number;\n itemLabel?: string;\n onItemChange?: (\n itemIndex: number,\n field: string,\n value: any,\n allItems: any[]\n ) => any[];\n isEmptyItem?: (item: any) => boolean;\n}\n\nexport interface CheckboxFieldConfig extends BaseFieldConfig {\n type: 'checkbox';\n size?: number;\n animateChange?: string;\n}\n\nexport interface MessageEditorFieldConfig extends BaseFieldConfig {\n type: 'message-editor';\n placeholder?: string;\n minHeight?: number;\n maxAttachments?: number;\n accept?: string;\n endpoint?: string;\n counter?: string;\n gsm?: boolean;\n autogrow?: boolean;\n disableCompletion?: boolean;\n}\n\nexport type FieldConfig =\n | TextFieldConfig\n | TextareaFieldConfig\n | SelectFieldConfig\n | KeyValueFieldConfig\n | ArrayFieldConfig\n | CheckboxFieldConfig\n | MessageEditorFieldConfig;\n\n// Layout configurations for better form organization\n// Recursive layout system - any layout item can contain other layout items\n\nexport interface FieldItemConfig {\n type: 'field';\n field: string; // field name to render\n}\n\nexport interface RowLayoutConfig {\n type: 'row';\n items: LayoutItem[]; // can contain fields, groups, or other rows\n gap?: string; // CSS gap value, defaults to '1rem'\n}\n\nexport interface GroupLayoutConfig {\n type: 'group';\n label: string;\n items: LayoutItem[]; // can contain fields, rows, or other groups\n collapsible?: boolean;\n collapsed?: boolean | ((formData: any) => boolean); // initial state if collapsible - can be a function\n helpText?: string;\n getGroupValueCount?: (formData: any) => number; // optional function to get count for bubble display\n}\n\nexport type LayoutItem =\n | FieldItemConfig\n | RowLayoutConfig\n | GroupLayoutConfig\n | string; // string is shorthand for field\n\nexport interface ActionConfig extends FormConfig {\n name: string;\n color: string;\n evaluated?: string[];\n render?: (node: any, action: any) => TemplateResult;\n\n form?: Record<string, FieldConfig>;\n layout?: LayoutItem[]; // optional layout configuration - array of layout items\n\n toFormData?: (action: Action) => any;\n fromFormData?: (formData: any) => Action;\n}\n\nexport const COLORS = {\n send: '#3498db',\n update: '#01c1af',\n broadcast: '#8e5ea7',\n call: '#e68628',\n create: '#df419f',\n save: '#1a777c',\n split: '#aaaaaa',\n execute: '#666666',\n wait: '#4d7dad',\n add: '#309c42',\n remove: '#e74c3c'\n};\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/flow/types.ts"],"names":[],"mappings":"AAkQA,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,SAAS;IACjB,SAAS,EAAE,SAAS;IACpB,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,SAAS;IACjB,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,SAAS;IACf,GAAG,EAAE,SAAS;IACd,MAAM,EAAE,SAAS;CAClB,CAAC","sourcesContent":["import { TemplateResult } from 'lit-html';\nimport { Action, Node } from '../store/flow-definition';\n\nexport interface ValidationResult {\n valid: boolean;\n errors: { [key: string]: string };\n}\n\n// Component attribute interfaces - these define what's allowed for each component type\nexport interface TextInputAttributes {\n type?: 'text' | 'email' | 'number' | 'url' | 'tel';\n placeholder?: string;\n clearable?: boolean;\n maxlength?: number;\n gsm?: boolean;\n autogrow?: boolean;\n textarea?: boolean;\n submitOnEnter?: boolean;\n}\n\nexport interface CompletionAttributes {\n placeholder?: string;\n clearable?: boolean;\n maxlength?: number;\n gsm?: boolean;\n autogrow?: boolean;\n textarea?: boolean;\n expressions?: string;\n counter?: string;\n minHeight?: number;\n}\n\nexport interface SelectAttributes {\n placeholder?: string;\n multi?: boolean;\n searchable?: boolean;\n tags?: boolean;\n emails?: boolean;\n clearable?: boolean;\n endpoint?: string;\n valueKey?: string;\n nameKey?: string;\n queryParam?: string;\n maxItems?: number;\n maxItemsText?: string;\n expressions?: string;\n options?: Array<{ name: string; value: any }>;\n sorted?: boolean;\n allowCreate?: boolean;\n jsonValue?: boolean;\n spaceSelect?: boolean;\n infoText?: string;\n}\n\nexport interface CheckboxAttributes {\n label?: string;\n size?: number;\n disabled?: boolean;\n animateChange?: string;\n}\n\nexport interface SliderAttributes {\n min?: number;\n max?: number;\n range?: boolean;\n}\n\nexport interface FormData extends Record<string, any> {}\n\nexport interface FormConfig {\n form?: Record<string, FieldConfig>;\n layout?: LayoutItem[];\n sanitize?: (formData: FormData) => void;\n validate?: (formData: FormData) => ValidationResult;\n}\n\nexport interface NodeConfig extends FormConfig {\n type: string;\n name?: string;\n color?: string;\n action?: ActionConfig;\n router?: {\n type: 'switch' | 'random';\n defaultCategory?: string;\n operand?: string;\n configurable?: boolean; // can the rules be configured in the UI\n rules?: {\n type:\n | 'has_number_between'\n | 'has_string'\n | 'has_value'\n | 'has_not_value'\n | 'has_text';\n arguments: string[];\n categoryName: string;\n }[];\n };\n\n toFormData?: (node: Node) => FormData;\n fromFormData?: (formData: FormData, originalNode: Node) => Node;\n render?: (node: Node) => TemplateResult;\n}\n\n// New field configuration system for generic form generation\nexport interface BaseFieldConfig {\n label?: string;\n required?: boolean;\n evaluated?: boolean;\n dependsOn?: string[];\n computeValue?: (\n values: Record<string, any>,\n currentValue: any,\n originalValues?: Record<string, any>\n ) => any;\n\n // Validation properties\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n helpText?: string;\n\n // Layout properties\n maxWidth?: string;\n\n // Conditional rendering\n conditions?: {\n visible?: (formData: Record<string, any>) => boolean;\n disabled?: (formData: Record<string, any>) => boolean;\n };\n}\n\nexport interface TextFieldConfig extends BaseFieldConfig {\n type: 'text';\n placeholder?: string;\n}\n\nexport interface TextareaFieldConfig extends BaseFieldConfig {\n type: 'textarea';\n placeholder?: string;\n rows?: number;\n minHeight?: number;\n}\n\nexport interface SelectFieldConfig extends BaseFieldConfig {\n type: 'select';\n options?: string[] | { value: string; name: string }[];\n multi?: boolean;\n clearable?: boolean;\n searchable?: boolean;\n tags?: boolean;\n placeholder?: string;\n maxItems?: number;\n valueKey?: string;\n nameKey?: string;\n endpoint?: string;\n emails?: boolean;\n getName?: (item: any) => string;\n flavor?: 'small' | 'large';\n createArbitraryOption?: (input: string, options: any[]) => any;\n allowCreate?: boolean;\n getDynamicOptions?: () => Array<{ value: string; name: string }>;\n}\n\nexport interface KeyValueFieldConfig extends BaseFieldConfig {\n type: 'key-value';\n sortable?: boolean;\n keyPlaceholder?: string;\n valuePlaceholder?: string;\n minRows?: number;\n}\n\nexport interface ArrayFieldConfig extends BaseFieldConfig {\n type: 'array';\n itemConfig: Record<string, FieldConfig>;\n sortable?: boolean;\n minItems?: number;\n maxItems?: number;\n itemLabel?: string;\n onItemChange?: (\n itemIndex: number,\n field: string,\n value: any,\n allItems: any[]\n ) => any[];\n isEmptyItem?: (item: any) => boolean;\n}\n\nexport interface CheckboxFieldConfig extends BaseFieldConfig {\n type: 'checkbox';\n size?: number;\n animateChange?: string;\n}\n\nexport interface MessageEditorFieldConfig extends BaseFieldConfig {\n type: 'message-editor';\n placeholder?: string;\n minHeight?: number;\n maxAttachments?: number;\n accept?: string;\n endpoint?: string;\n counter?: string;\n gsm?: boolean;\n autogrow?: boolean;\n disableCompletion?: boolean;\n}\n\nexport type FieldConfig =\n | TextFieldConfig\n | TextareaFieldConfig\n | SelectFieldConfig\n | KeyValueFieldConfig\n | ArrayFieldConfig\n | CheckboxFieldConfig\n | MessageEditorFieldConfig;\n\n// Layout configurations for better form organization\n// Recursive layout system - any layout item can contain other layout items\n\nexport interface FieldItemConfig {\n type: 'field';\n field: string; // field name to render\n}\n\nexport interface RowLayoutConfig {\n type: 'row';\n items: LayoutItem[]; // can contain fields, groups, or other rows\n gap?: string; // CSS gap value, defaults to '1rem'\n}\n\nexport interface GroupLayoutConfig {\n type: 'group';\n label: string;\n items: LayoutItem[]; // can contain fields, rows, or other groups\n collapsible?: boolean;\n collapsed?: boolean | ((formData: any) => boolean); // initial state if collapsible - can be a function\n helpText?: string;\n getGroupValueCount?: (formData: any) => number; // optional function to get count for bubble display\n}\n\nexport type LayoutItem =\n | FieldItemConfig\n | RowLayoutConfig\n | GroupLayoutConfig\n | string; // string is shorthand for field\n\nexport interface ActionConfig extends FormConfig {\n name: string;\n color: string;\n evaluated?: string[];\n render?: (node: any, action: any) => TemplateResult;\n\n form?: Record<string, FieldConfig>;\n layout?: LayoutItem[]; // optional layout configuration - array of layout items\n\n toFormData?: (action: Action) => any;\n fromFormData?: (formData: any) => Action;\n}\n\nexport const COLORS = {\n send: '#3498db',\n update: '#01c1af',\n broadcast: '#8e5ea7',\n call: '#e68628',\n create: '#df419f',\n save: '#1a777c',\n split: '#aaaaaa',\n execute: '#666666',\n wait: '#4d7dad',\n add: '#309c42',\n remove: '#e74c3c'\n};\n"]}
@@ -65,12 +65,13 @@ let TembaArrayEditor = class TembaArrayEditor extends BaseListEditor {
65
65
  return config.computeValue(item, currentValue);
66
66
  }
67
67
  // For select fields, ensure we return the right type
68
- if (config.type === 'select') {
69
- const selectConfig = config;
70
- if (currentValue === undefined || currentValue === null) {
71
- return selectConfig.multi ? [] : '';
72
- }
73
- }
68
+ /*if (config.type === 'select') {
69
+ console.log('computeFieldValue select', currentValue, config);
70
+ const selectConfig = config as SelectFieldConfig;
71
+ if (currentValue === undefined || currentValue === null) {
72
+ return selectConfig.multi ? [] : '';
73
+ }
74
+ }*/
74
75
  return currentValue;
75
76
  }
76
77
  renderArrayField(itemIndex, fieldName, config) {
@@ -85,25 +86,8 @@ let TembaArrayEditor = class TembaArrayEditor extends BaseListEditor {
85
86
  const target = e.target;
86
87
  // Handle different field types and their change events
87
88
  if (config.type === 'select') {
88
- // For temba-select, extract the correct value
89
- if (target.tagName === 'TEMBA-SELECT') {
90
- if (target.multi || target.emails || target.tags) {
91
- value = target.values || [];
92
- }
93
- else {
94
- // Single select: extract value from first selected option
95
- const values = target.values || [];
96
- value =
97
- values.length > 0 && values[0]
98
- ? values[0].value !== undefined
99
- ? values[0].value
100
- : values[0]
101
- : '';
102
- }
103
- }
104
- else {
105
- value = target.value;
106
- }
89
+ // Use consistent temba-select value normalization
90
+ value = target.values;
107
91
  }
108
92
  else {
109
93
  // For other field types, use the target value directly
@@ -1 +1 @@
1
- {"version":3,"file":"ArrayEditor.js","sourceRoot":"","sources":["../../../src/form/ArrayEditor.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css, TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { FieldConfig, SelectFieldConfig } from '../flow/types';\nimport { BaseListEditor, ListItem } from './BaseListEditor';\nimport { FieldRenderer } from './FieldRenderer';\n\n@customElement('temba-array-editor')\nexport class TembaArrayEditor extends BaseListEditor<ListItem> {\n @property({ type: Object })\n itemConfig: Record<string, FieldConfig> = {};\n\n @property({ type: String })\n itemLabel = 'Item';\n\n @property({ type: Function })\n onItemChange?: (\n itemIndex: number,\n field: string,\n value: any,\n allItems: any[]\n ) => any[];\n\n @property({ type: Function })\n isEmptyItemFn?: (item: any) => boolean;\n\n @property({ type: Boolean })\n maintainEmptyItem = true; // Enable by default for better UX\n\n constructor() {\n super();\n this._items = [];\n }\n\n // External API\n @property({ type: Array })\n get value(): any[] {\n return [...this._items];\n }\n\n set value(newValue: any[]) {\n this._items = newValue || [];\n this.requestUpdate();\n }\n\n // Implement abstract methods\n isEmptyItem(item: ListItem): boolean {\n // Use configurable function if provided\n if (this.isEmptyItemFn) {\n return this.isEmptyItemFn(item);\n }\n\n // Default behavior: check if all values are empty\n const values = Object.values(item);\n if (values.length === 0) {\n return true;\n }\n\n return values.every(\n (value) => value === undefined || value === null || value === ''\n );\n }\n\n // Override cleanItems to be more permissive for form data\n protected cleanItems(items: ListItem[]): any {\n // For runtime attachments, keep items that have at least one non-empty field\n return items.filter((item) => {\n const values = Object.values(item);\n return (\n values.length > 0 &&\n values.some(\n (value) => value !== undefined && value !== null && value !== ''\n )\n );\n });\n }\n\n createEmptyItem(): ListItem {\n return {};\n }\n\n protected handleFieldChange(\n itemIndex: number,\n fieldName: string,\n newValue: any\n ) {\n let updatedItems: any[];\n\n if (this.onItemChange) {\n updatedItems = this.onItemChange(\n itemIndex,\n fieldName,\n newValue,\n this._items\n );\n } else {\n updatedItems = [...this._items];\n updatedItems[itemIndex] = {\n ...updatedItems[itemIndex],\n [fieldName]: newValue\n };\n }\n\n this.updateValue(updatedItems);\n }\n\n private computeFieldValue(\n itemIndex: number,\n fieldName: string,\n config: FieldConfig\n ): any {\n const item = this._items[itemIndex] || {};\n const currentValue = item[fieldName];\n\n if (config.computeValue) {\n return config.computeValue(item, currentValue);\n }\n\n // For select fields, ensure we return the right type\n if (config.type === 'select') {\n const selectConfig = config as SelectFieldConfig;\n if (currentValue === undefined || currentValue === null) {\n return selectConfig.multi ? [] : '';\n }\n }\n\n return currentValue;\n }\n\n private renderArrayField(\n itemIndex: number,\n fieldName: string,\n config: FieldConfig\n ): TemplateResult {\n const computedValue = this.computeFieldValue(itemIndex, fieldName, config);\n\n // Use FieldRenderer for consistent field rendering\n return FieldRenderer.renderField(fieldName, config, computedValue, {\n showLabel: false, // ArrayEditor doesn't show labels for individual fields\n flavor: 'small', // ArrayEditor uses small flavor\n extraClasses: 'form-control',\n onChange: (e: Event) => {\n let value: any;\n const target = e.target as any;\n\n // Handle different field types and their change events\n if (config.type === 'select') {\n // For temba-select, extract the correct value\n if (target.tagName === 'TEMBA-SELECT') {\n if (target.multi || target.emails || target.tags) {\n value = target.values || [];\n } else {\n // Single select: extract value from first selected option\n const values = target.values || [];\n value =\n values.length > 0 && values[0]\n ? values[0].value !== undefined\n ? values[0].value\n : values[0]\n : '';\n }\n } else {\n value = target.value;\n }\n } else {\n // For other field types, use the target value directly\n value = target.value;\n }\n\n this.handleFieldChange(itemIndex, fieldName, value);\n }\n });\n }\n\n renderItem(item: ListItem, index: number): TemplateResult {\n const canRemove = this.canRemoveItem(index);\n\n return html`\n <div class=\"array-item\">\n <div class=\"item-fields\">\n ${Object.entries(this.itemConfig).map(\n ([fieldName, config]) => html`\n <div class=\"field\">\n ${this.renderArrayField(index, fieldName, config)}\n </div>\n `\n )}\n ${canRemove\n ? html`\n <button\n @click=${() => this.removeItem(index)}\n class=\"remove-btn\"\n >\n <temba-icon name=\"x\"></temba-icon>\n </button>\n `\n : ''}\n </div>\n </div>\n `;\n }\n\n protected getContainerClass(): string {\n return 'array-editor';\n }\n\n static get styles() {\n return css`\n ${super.styles}\n\n .array-editor {\n }\n\n .array-item {\n }\n\n .item-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n .item-title {\n font-weight: 600;\n color: #333;\n }\n\n .item-fields {\n display: flex;\n gap: 12px;\n align-items: center;\n }\n\n .field {\n flex: 1;\n }\n\n .add-btn,\n .remove-btn {\n padding: 8px;\n border: 1px solid #ccc;\n border-radius: 4px;\n background: white;\n cursor: pointer;\n font-size: 14px;\n }\n\n .add-btn:hover,\n .remove-btn:hover {\n background: #f8f8f8;\n }\n\n .remove-btn {\n background: #fefefe;\n color: #999;\n }\n `;\n }\n}\n"]}
1
+ {"version":3,"file":"ArrayEditor.js","sourceRoot":"","sources":["../../../src/form/ArrayEditor.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css, TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { FieldConfig } from '../flow/types';\nimport { BaseListEditor, ListItem } from './BaseListEditor';\nimport { FieldRenderer } from './FieldRenderer';\n\n@customElement('temba-array-editor')\nexport class TembaArrayEditor extends BaseListEditor<ListItem> {\n @property({ type: Object })\n itemConfig: Record<string, FieldConfig> = {};\n\n @property({ type: String })\n itemLabel = 'Item';\n\n @property({ type: Function })\n onItemChange?: (\n itemIndex: number,\n field: string,\n value: any,\n allItems: any[]\n ) => any[];\n\n @property({ type: Function })\n isEmptyItemFn?: (item: any) => boolean;\n\n @property({ type: Boolean })\n maintainEmptyItem = true; // Enable by default for better UX\n\n constructor() {\n super();\n this._items = [];\n }\n\n // External API\n @property({ type: Array })\n get value(): any[] {\n return [...this._items];\n }\n\n set value(newValue: any[]) {\n this._items = newValue || [];\n this.requestUpdate();\n }\n\n // Implement abstract methods\n isEmptyItem(item: ListItem): boolean {\n // Use configurable function if provided\n if (this.isEmptyItemFn) {\n return this.isEmptyItemFn(item);\n }\n\n // Default behavior: check if all values are empty\n const values = Object.values(item);\n if (values.length === 0) {\n return true;\n }\n\n return values.every(\n (value) => value === undefined || value === null || value === ''\n );\n }\n\n // Override cleanItems to be more permissive for form data\n protected cleanItems(items: ListItem[]): any {\n // For runtime attachments, keep items that have at least one non-empty field\n return items.filter((item) => {\n const values = Object.values(item);\n return (\n values.length > 0 &&\n values.some(\n (value) => value !== undefined && value !== null && value !== ''\n )\n );\n });\n }\n\n createEmptyItem(): ListItem {\n return {};\n }\n\n protected handleFieldChange(\n itemIndex: number,\n fieldName: string,\n newValue: any\n ) {\n let updatedItems: any[];\n\n if (this.onItemChange) {\n updatedItems = this.onItemChange(\n itemIndex,\n fieldName,\n newValue,\n this._items\n );\n } else {\n updatedItems = [...this._items];\n updatedItems[itemIndex] = {\n ...updatedItems[itemIndex],\n [fieldName]: newValue\n };\n }\n\n this.updateValue(updatedItems);\n }\n\n private computeFieldValue(\n itemIndex: number,\n fieldName: string,\n config: FieldConfig\n ): any {\n const item = this._items[itemIndex] || {};\n const currentValue = item[fieldName];\n\n if (config.computeValue) {\n return config.computeValue(item, currentValue);\n }\n\n // For select fields, ensure we return the right type\n /*if (config.type === 'select') {\n console.log('computeFieldValue select', currentValue, config);\n const selectConfig = config as SelectFieldConfig;\n if (currentValue === undefined || currentValue === null) {\n return selectConfig.multi ? [] : '';\n }\n }*/\n\n return currentValue;\n }\n\n private renderArrayField(\n itemIndex: number,\n fieldName: string,\n config: FieldConfig\n ): TemplateResult {\n const computedValue = this.computeFieldValue(itemIndex, fieldName, config);\n\n // Use FieldRenderer for consistent field rendering\n return FieldRenderer.renderField(fieldName, config, computedValue, {\n showLabel: false, // ArrayEditor doesn't show labels for individual fields\n flavor: 'small', // ArrayEditor uses small flavor\n extraClasses: 'form-control',\n onChange: (e: Event) => {\n let value: any;\n const target = e.target as any;\n\n // Handle different field types and their change events\n if (config.type === 'select') {\n // Use consistent temba-select value normalization\n value = target.values;\n } else {\n // For other field types, use the target value directly\n value = target.value;\n }\n\n this.handleFieldChange(itemIndex, fieldName, value);\n }\n });\n }\n\n renderItem(item: ListItem, index: number): TemplateResult {\n const canRemove = this.canRemoveItem(index);\n\n return html`\n <div class=\"array-item\">\n <div class=\"item-fields\">\n ${Object.entries(this.itemConfig).map(\n ([fieldName, config]) => html`\n <div class=\"field\">\n ${this.renderArrayField(index, fieldName, config)}\n </div>\n `\n )}\n ${canRemove\n ? html`\n <button\n @click=${() => this.removeItem(index)}\n class=\"remove-btn\"\n >\n <temba-icon name=\"x\"></temba-icon>\n </button>\n `\n : ''}\n </div>\n </div>\n `;\n }\n\n protected getContainerClass(): string {\n return 'array-editor';\n }\n\n static get styles() {\n return css`\n ${super.styles}\n\n .array-editor {\n }\n\n .array-item {\n }\n\n .item-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n .item-title {\n font-weight: 600;\n color: #333;\n }\n\n .item-fields {\n display: flex;\n gap: 12px;\n align-items: center;\n }\n\n .field {\n flex: 1;\n }\n\n .add-btn,\n .remove-btn {\n padding: 8px;\n border: 1px solid #ccc;\n border-radius: 4px;\n background: white;\n cursor: pointer;\n font-size: 14px;\n }\n\n .add-btn:hover,\n .remove-btn:hover {\n background: #f8f8f8;\n }\n\n .remove-btn {\n background: #fefefe;\n color: #999;\n }\n `;\n }\n}\n"]}
@@ -110,75 +110,17 @@ export class FieldRenderer {
110
110
  ></temba-textinput>`;
111
111
  }
112
112
  static renderSelect(fieldName, config, value, context) {
113
- var _a, _b;
114
113
  const { errors = [], onChange, showLabel = true, flavor, extraClasses, style } = context;
115
- // Ensure proper value handling for multi vs single select
116
- const normalizedValue = (() => {
117
- if (config.multi) {
118
- // Multi-select: ensure we have an array and convert strings to option objects
119
- const valueArray = Array.isArray(value) ? value : value ? [value] : [];
120
- return valueArray.map((val) => {
121
- if (typeof val === 'string') {
122
- // Convert string values to option objects
123
- return { name: val, value: val };
124
- }
125
- return val;
126
- });
127
- }
128
- else {
129
- // Single select: use the value as-is
130
- return value || '';
131
- }
132
- })();
133
- if (typeof normalizedValue === 'string') {
134
- return html `<temba-select
135
- name="${fieldName}"
136
- ?required="${config.required}"
137
- .errors="${errors}"
138
- value="${config.multi ? '' : normalizedValue}"
139
- .values="${config.multi ? normalizedValue : undefined}"
140
- ?multi="${config.multi}"
141
- ?searchable="${config.searchable}"
142
- ?tags="${config.tags}"
143
- ?emails="${config.emails}"
144
- ?clearable="${config.clearable || false}"
145
- label="${showLabel ? config.label : ''}"
146
- placeholder="${config.placeholder || ''}"
147
- maxItems="${config.maxItems || 0}"
148
- valueKey="${config.valueKey || 'value'}"
149
- nameKey="${config.nameKey || 'name'}"
150
- endpoint="${config.endpoint || ''}"
151
- .helpText="${config.helpText || ''}"
152
- flavor="${flavor || config.flavor || 'small'}"
153
- class="${extraClasses}"
154
- style="${style}"
155
- .getName=${config.getName}
156
- .createArbitraryOption=${config.createArbitraryOption}
157
- ?allowCreate="${config.allowCreate || false}"
158
- @change="${onChange || (() => { })}"
159
- >
160
- ${(_a = config.options) === null || _a === void 0 ? void 0 : _a.map((option) => {
161
- if (typeof option === 'string') {
162
- return html `<temba-option
163
- name="${option}"
164
- value="${option}"
165
- ></temba-option>`;
166
- }
167
- else {
168
- return html `<temba-option
169
- name="${option.label || option.name}"
170
- value="${option.value}"
171
- ></temba-option>`;
172
- }
173
- })}
174
- </temba-select>`;
175
- }
114
+ // Get options - use dynamic options if available, otherwise use static options
115
+ const optionsToRender = config.getDynamicOptions
116
+ ? config.getDynamicOptions()
117
+ : config.options;
176
118
  return html `<temba-select
177
119
  name="${fieldName}"
178
120
  label="${showLabel ? config.label : ''}"
179
121
  ?required="${config.required}"
180
122
  .errors="${errors}"
181
- .values="${normalizedValue}"
123
+ .values=${value}
182
124
  ?multi="${config.multi}"
183
125
  ?searchable="${config.searchable}"
184
126
  ?tags="${config.tags}"
@@ -198,7 +140,7 @@ export class FieldRenderer {
198
140
  ?allowCreate="${config.allowCreate || false}"
199
141
  @change="${onChange || (() => { })}"
200
142
  >
201
- ${(_b = config.options) === null || _b === void 0 ? void 0 : _b.map((option) => {
143
+ ${optionsToRender === null || optionsToRender === void 0 ? void 0 : optionsToRender.map((option) => {
202
144
  if (typeof option === 'string') {
203
145
  return html `<temba-option
204
146
  name="${option}"