@nyaruka/temba-components 0.142.0 → 0.142.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/temba-components.js +825 -654
  3. package/dist/temba-components.js.map +1 -1
  4. package/out-tsc/src/Icons.js +1 -0
  5. package/out-tsc/src/Icons.js.map +1 -1
  6. package/out-tsc/src/flow/CanvasMenu.js +30 -35
  7. package/out-tsc/src/flow/CanvasMenu.js.map +1 -1
  8. package/out-tsc/src/flow/CanvasNode.js +13 -8
  9. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  10. package/out-tsc/src/flow/Editor.js +18 -5
  11. package/out-tsc/src/flow/Editor.js.map +1 -1
  12. package/out-tsc/src/flow/NodeEditor.js +346 -10
  13. package/out-tsc/src/flow/NodeEditor.js.map +1 -1
  14. package/out-tsc/src/flow/NodeTypeSelector.js +2 -0
  15. package/out-tsc/src/flow/NodeTypeSelector.js.map +1 -1
  16. package/out-tsc/src/flow/Plumber.js +3 -1
  17. package/out-tsc/src/flow/Plumber.js.map +1 -1
  18. package/out-tsc/src/flow/actions/add_contact_urn.js +2 -6
  19. package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
  20. package/out-tsc/src/flow/actions/enter_flow.js +2 -2
  21. package/out-tsc/src/flow/actions/enter_flow.js.map +1 -1
  22. package/out-tsc/src/flow/actions/say_msg.js +2 -1
  23. package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
  24. package/out-tsc/src/flow/actions/send_broadcast.js +2 -6
  25. package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
  26. package/out-tsc/src/flow/actions/send_email.js +2 -6
  27. package/out-tsc/src/flow/actions/send_email.js.map +1 -1
  28. package/out-tsc/src/flow/actions/send_msg.js +52 -35
  29. package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
  30. package/out-tsc/src/flow/actions/set_contact_channel.js +2 -1
  31. package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
  32. package/out-tsc/src/flow/actions/set_contact_field.js +4 -5
  33. package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
  34. package/out-tsc/src/flow/actions/set_contact_language.js +3 -3
  35. package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
  36. package/out-tsc/src/flow/actions/set_contact_name.js +2 -1
  37. package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
  38. package/out-tsc/src/flow/actions/set_contact_status.js +2 -1
  39. package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
  40. package/out-tsc/src/flow/actions/set_run_result.js +3 -3
  41. package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
  42. package/out-tsc/src/flow/actions/start_session.js +2 -2
  43. package/out-tsc/src/flow/actions/start_session.js.map +1 -1
  44. package/out-tsc/src/flow/nodes/split_by_llm.js +4 -5
  45. package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -1
  46. package/out-tsc/src/flow/nodes/split_by_resthook.js +3 -8
  47. package/out-tsc/src/flow/nodes/split_by_resthook.js.map +1 -1
  48. package/out-tsc/src/flow/nodes/split_by_subflow.js +2 -2
  49. package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
  50. package/out-tsc/src/flow/nodes/split_by_webhook.js +25 -33
  51. package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
  52. package/out-tsc/src/flow/nodes/wait_for_response.js +1 -0
  53. package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
  54. package/out-tsc/src/flow/types.js.map +1 -1
  55. package/out-tsc/src/flow/utils.js +68 -0
  56. package/out-tsc/src/flow/utils.js.map +1 -1
  57. package/out-tsc/src/form/FieldRenderer.js +17 -2
  58. package/out-tsc/src/form/FieldRenderer.js.map +1 -1
  59. package/out-tsc/src/interfaces.js +1 -0
  60. package/out-tsc/src/interfaces.js.map +1 -1
  61. package/out-tsc/src/simulator/Simulator.js +1 -1
  62. package/out-tsc/src/simulator/Simulator.js.map +1 -1
  63. package/out-tsc/src/utils.js +5 -12
  64. package/out-tsc/src/utils.js.map +1 -1
  65. package/out-tsc/test/nodes/split_by_run_result.test.js +1 -2
  66. package/out-tsc/test/nodes/split_by_run_result.test.js.map +1 -1
  67. package/out-tsc/test/temba-canvas-menu.test.js +13 -9
  68. package/out-tsc/test/temba-canvas-menu.test.js.map +1 -1
  69. package/out-tsc/test/temba-flow-reflow.test.js.map +1 -1
  70. package/out-tsc/test/temba-node-editor.test.js +9 -10
  71. package/out-tsc/test/temba-node-editor.test.js.map +1 -1
  72. package/out-tsc/test/temba-node-type-selector.test.js +3 -3
  73. package/out-tsc/test/temba-node-type-selector.test.js.map +1 -1
  74. package/out-tsc/test/temba-simulator.test.js +2 -2
  75. package/out-tsc/test/temba-simulator.test.js.map +1 -1
  76. package/package.json +2 -2
  77. package/screenshots/truth/actions/enter_flow/render/basic-flow.png +0 -0
  78. package/screenshots/truth/actions/enter_flow/render/long-flow-name.png +0 -0
  79. package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
  80. package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
  81. package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
  82. package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
  83. package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
  84. package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
  85. package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
  86. package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
  87. package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
  88. package/screenshots/truth/actions/start_session/render/contact-query.png +0 -0
  89. package/screenshots/truth/actions/start_session/render/contacts-only.png +0 -0
  90. package/screenshots/truth/actions/start_session/render/create-contact.png +0 -0
  91. package/screenshots/truth/actions/start_session/render/groups-and-contacts.png +0 -0
  92. package/screenshots/truth/actions/start_session/render/groups-only.png +0 -0
  93. package/screenshots/truth/actions/start_session/render/many-recipients.png +0 -0
  94. package/screenshots/truth/canvas-menu/open.png +0 -0
  95. package/screenshots/truth/node-type-selector/action-mode.png +0 -0
  96. package/screenshots/truth/node-type-selector/split-mode.png +0 -0
  97. package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
  98. package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
  99. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  100. package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
  101. package/src/Icons.ts +1 -0
  102. package/src/flow/CanvasMenu.ts +38 -39
  103. package/src/flow/CanvasNode.ts +16 -8
  104. package/src/flow/Editor.ts +33 -6
  105. package/src/flow/NodeEditor.ts +373 -10
  106. package/src/flow/NodeTypeSelector.ts +2 -0
  107. package/src/flow/Plumber.ts +3 -1
  108. package/src/flow/actions/add_contact_urn.ts +5 -6
  109. package/src/flow/actions/enter_flow.ts +2 -2
  110. package/src/flow/actions/say_msg.ts +2 -1
  111. package/src/flow/actions/send_broadcast.ts +2 -6
  112. package/src/flow/actions/send_email.ts +2 -6
  113. package/src/flow/actions/send_msg.ts +56 -38
  114. package/src/flow/actions/set_contact_channel.ts +5 -1
  115. package/src/flow/actions/set_contact_field.ts +10 -5
  116. package/src/flow/actions/set_contact_language.ts +6 -3
  117. package/src/flow/actions/set_contact_name.ts +5 -1
  118. package/src/flow/actions/set_contact_status.ts +5 -1
  119. package/src/flow/actions/set_run_result.ts +6 -3
  120. package/src/flow/actions/start_session.ts +2 -2
  121. package/src/flow/nodes/split_by_llm.ts +5 -5
  122. package/src/flow/nodes/split_by_resthook.ts +3 -8
  123. package/src/flow/nodes/split_by_subflow.ts +2 -2
  124. package/src/flow/nodes/split_by_webhook.ts +26 -34
  125. package/src/flow/nodes/wait_for_response.ts +1 -0
  126. package/src/flow/types.ts +25 -2
  127. package/src/flow/utils.ts +81 -1
  128. package/src/form/FieldRenderer.ts +32 -3
  129. package/src/interfaces.ts +1 -0
  130. package/src/simulator/Simulator.ts +1 -1
  131. package/src/utils.ts +5 -12
  132. package/test/nodes/split_by_run_result.test.ts +1 -2
  133. package/test/temba-canvas-menu.test.ts +13 -9
  134. package/test/temba-flow-reflow.test.ts +4 -2
  135. package/test/temba-node-editor.test.ts +9 -10
  136. package/test/temba-node-type-selector.test.ts +3 -3
  137. package/test/temba-simulator.test.ts +2 -2
@@ -1,6 +1,6 @@
1
1
  import { html } from 'lit-html';
2
2
  import { ACTION_GROUPS, FlowTypes } from '../types';
3
- import { SCHEMES } from '../utils';
3
+ import { SCHEMES, renderClamped } from '../utils';
4
4
  export const add_contact_urn = {
5
5
  name: 'Add URN',
6
6
  group: ACTION_GROUPS.contacts,
@@ -8,11 +8,7 @@ export const add_contact_urn = {
8
8
  render: (_node, action) => {
9
9
  const schemeObj = SCHEMES.find((s) => s.scheme === action.scheme);
10
10
  const friendlyScheme = (schemeObj === null || schemeObj === void 0 ? void 0 : schemeObj.path) || action.scheme;
11
- return html `<div
12
- style="word-wrap: break-word; overflow-wrap: break-word; hyphens: auto;"
13
- >
14
- Add ${friendlyScheme} <strong>${action.path}</strong>
15
- </div>`;
11
+ return renderClamped(html `Add ${friendlyScheme} <strong>${action.path}</strong>`, `Add ${friendlyScheme} ${action.path}`);
16
12
  },
17
13
  toFormData: (action) => {
18
14
  const schemeObj = SCHEMES.find((s) => s.scheme === action.scheme);
@@ -1 +1 @@
1
- {"version":3,"file":"add_contact_urn.js","sourceRoot":"","sources":["../../../../src/flow/actions/add_contact_urn.ts"],"names":[],"mappings":"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","sourcesContent":["import { html } from 'lit-html';\nimport {\n ActionConfig,\n ACTION_GROUPS,\n FormData,\n ValidationResult,\n FlowTypes\n} from '../types';\nimport { Node, AddContactUrn } from '../../store/flow-definition';\nimport { SCHEMES } from '../utils';\n\nexport const add_contact_urn: ActionConfig = {\n name: 'Add URN',\n group: ACTION_GROUPS.contacts,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n render: (_node: Node, action: AddContactUrn) => {\n const schemeObj = SCHEMES.find((s) => s.scheme === action.scheme);\n const friendlyScheme = schemeObj?.path || action.scheme;\n return html`<div\n style=\"word-wrap: break-word; overflow-wrap: break-word; hyphens: auto;\"\n >\n Add ${friendlyScheme} <strong>${action.path}</strong>\n </div>`;\n },\n\n toFormData: (action: AddContactUrn) => {\n const schemeObj = SCHEMES.find((s) => s.scheme === action.scheme);\n return {\n uuid: action.uuid,\n scheme: schemeObj\n ? [{ name: schemeObj.path, value: action.scheme }]\n : null,\n path: action.path || ''\n };\n },\n\n fromFormData: (formData: FormData): AddContactUrn => {\n // Extract scheme value from select format\n const schemeValue =\n Array.isArray(formData.scheme) && formData.scheme.length > 0\n ? formData.scheme[0].value\n : 'tel';\n\n return {\n uuid: formData.uuid,\n type: 'add_contact_urn',\n scheme: schemeValue,\n path: formData.path || ''\n };\n },\n\n form: {\n scheme: {\n type: 'select',\n label: 'URN Type',\n helpText: 'Select the type of URN to add to the contact',\n required: true,\n searchable: false,\n multi: false,\n options: SCHEMES.map((scheme) => ({\n name: scheme.path,\n value: scheme.scheme\n }))\n },\n path: {\n type: 'text',\n label: 'URN Value',\n helpText: 'Enter the URN value (e.g., phone number, Facebook ID, etc.)',\n required: true,\n placeholder: 'Enter the URN value...',\n evaluated: true\n }\n },\n\n validate: (formData: FormData): ValidationResult => {\n const errors: { [key: string]: string } = {};\n\n if (!formData.scheme || formData.scheme.length === 0) {\n errors.scheme = 'URN type is required';\n }\n\n if (!formData.path || formData.path.trim() === '') {\n errors.path = 'URN value is required';\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors\n };\n }\n};\n"]}
1
+ {"version":3,"file":"add_contact_urn.js","sourceRoot":"","sources":["../../../../src/flow/actions/add_contact_urn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAEL,aAAa,EAGb,SAAS,EACV,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAElD,MAAM,CAAC,MAAM,eAAe,GAAiB;IAC3C,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,aAAa,CAAC,QAAQ;IAC7B,SAAS,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,UAAU,CAAC;IACrE,MAAM,EAAE,CAAC,KAAW,EAAE,MAAqB,EAAE,EAAE;QAC7C,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC;QAClE,MAAM,cAAc,GAAG,CAAA,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,IAAI,KAAI,MAAM,CAAC,MAAM,CAAC;QACxD,OAAO,aAAa,CAClB,IAAI,CAAA,OAAO,cAAc,YAAY,MAAM,CAAC,IAAI,WAAW,EAC3D,OAAO,cAAc,IAAI,MAAM,CAAC,IAAI,EAAE,CACvC,CAAC;IACJ,CAAC;IAED,UAAU,EAAE,CAAC,MAAqB,EAAE,EAAE;QACpC,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC;QAClE,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,MAAM,EAAE,SAAS;gBACf,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClD,CAAC,CAAC,IAAI;YACR,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE;SACxB,CAAC;IACJ,CAAC;IAED,YAAY,EAAE,CAAC,QAAkB,EAAiB,EAAE;QAClD,0CAA0C;QAC1C,MAAM,WAAW,GACf,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;YAC1D,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK;YAC1B,CAAC,CAAC,KAAK,CAAC;QAEZ,OAAO;YACL,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,IAAI,EAAE,iBAAiB;YACvB,MAAM,EAAE,WAAW;YACnB,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,EAAE;SAC1B,CAAC;IACJ,CAAC;IAED,IAAI,EAAE;QACJ,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,UAAU;YACjB,QAAQ,EAAE,8CAA8C;YACxD,QAAQ,EAAE,IAAI;YACd,UAAU,EAAE,KAAK;YACjB,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;gBAChC,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,KAAK,EAAE,MAAM,CAAC,MAAM;aACrB,CAAC,CAAC;SACJ;QACD,IAAI,EAAE;YACJ,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,WAAW;YAClB,QAAQ,EAAE,6DAA6D;YACvE,QAAQ,EAAE,IAAI;YACd,WAAW,EAAE,wBAAwB;YACrC,SAAS,EAAE,IAAI;SAChB;KACF;IAED,QAAQ,EAAE,CAAC,QAAkB,EAAoB,EAAE;QACjD,MAAM,MAAM,GAA8B,EAAE,CAAC;QAE7C,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrD,MAAM,CAAC,MAAM,GAAG,sBAAsB,CAAC;QACzC,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAClD,MAAM,CAAC,IAAI,GAAG,uBAAuB,CAAC;QACxC,CAAC;QAED,OAAO;YACL,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC;YACvC,MAAM;SACP,CAAC;IACJ,CAAC;CACF,CAAC","sourcesContent":["import { html } from 'lit-html';\nimport {\n ActionConfig,\n ACTION_GROUPS,\n FormData,\n ValidationResult,\n FlowTypes\n} from '../types';\nimport { Node, AddContactUrn } from '../../store/flow-definition';\nimport { SCHEMES, renderClamped } from '../utils';\n\nexport const add_contact_urn: ActionConfig = {\n name: 'Add URN',\n group: ACTION_GROUPS.contacts,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n render: (_node: Node, action: AddContactUrn) => {\n const schemeObj = SCHEMES.find((s) => s.scheme === action.scheme);\n const friendlyScheme = schemeObj?.path || action.scheme;\n return renderClamped(\n html`Add ${friendlyScheme} <strong>${action.path}</strong>`,\n `Add ${friendlyScheme} ${action.path}`\n );\n },\n\n toFormData: (action: AddContactUrn) => {\n const schemeObj = SCHEMES.find((s) => s.scheme === action.scheme);\n return {\n uuid: action.uuid,\n scheme: schemeObj\n ? [{ name: schemeObj.path, value: action.scheme }]\n : null,\n path: action.path || ''\n };\n },\n\n fromFormData: (formData: FormData): AddContactUrn => {\n // Extract scheme value from select format\n const schemeValue =\n Array.isArray(formData.scheme) && formData.scheme.length > 0\n ? formData.scheme[0].value\n : 'tel';\n\n return {\n uuid: formData.uuid,\n type: 'add_contact_urn',\n scheme: schemeValue,\n path: formData.path || ''\n };\n },\n\n form: {\n scheme: {\n type: 'select',\n label: 'URN Type',\n helpText: 'Select the type of URN to add to the contact',\n required: true,\n searchable: false,\n multi: false,\n options: SCHEMES.map((scheme) => ({\n name: scheme.path,\n value: scheme.scheme\n }))\n },\n path: {\n type: 'text',\n label: 'URN Value',\n helpText: 'Enter the URN value (e.g., phone number, Facebook ID, etc.)',\n required: true,\n placeholder: 'Enter the URN value...',\n evaluated: true\n }\n },\n\n validate: (formData: FormData): ValidationResult => {\n const errors: { [key: string]: string } = {};\n\n if (!formData.scheme || formData.scheme.length === 0) {\n errors.scheme = 'URN type is required';\n }\n\n if (!formData.path || formData.path.trim() === '') {\n errors.path = 'URN value is required';\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors\n };\n }\n};\n"]}
@@ -1,13 +1,13 @@
1
1
  import { html } from 'lit-html';
2
2
  import { ACTION_GROUPS, FlowTypes } from '../types';
3
- import { renderNamedObjects } from '../utils';
3
+ import { renderFlowLinks } from '../utils';
4
4
  export const enter_flow = {
5
5
  name: 'Enter a Flow',
6
6
  group: ACTION_GROUPS.trigger,
7
7
  hideFromActions: true,
8
8
  flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],
9
9
  render: (_node, action) => {
10
- return html `${renderNamedObjects([action.flow], 'flow')}`;
10
+ return html `${renderFlowLinks([action.flow], 'flow')}`;
11
11
  },
12
12
  toFormData: (action) => {
13
13
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"enter_flow.js","sourceRoot":"","sources":["../../../../src/flow/actions/enter_flow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAgB,aAAa,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAElE,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAE9C,MAAM,CAAC,MAAM,UAAU,GAAiB;IACtC,IAAI,EAAE,cAAc;IACpB,KAAK,EAAE,aAAa,CAAC,OAAO;IAC5B,eAAe,EAAE,IAAI;IACrB,SAAS,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,UAAU,CAAC;IACrE,MAAM,EAAE,CAAC,KAAW,EAAE,MAAiB,EAAE,EAAE;QACzC,OAAO,IAAI,CAAA,GAAG,kBAAkB,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC;IAC5D,CAAC;IACD,UAAU,EAAE,CAAC,MAAiB,EAAE,EAAE;QAChC,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE;SACvC,CAAC;IACJ,CAAC;IACD,IAAI,EAAE;QACJ,IAAI,EAAE;YACJ,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,IAAI;YACd,WAAW,EAAE,kBAAkB;YAC/B,QAAQ,EAAE,iDAAiD;YAC3D,QAAQ,EAAE,oBAAoB;YAC9B,QAAQ,EAAE,MAAM;YAChB,OAAO,EAAE,MAAM;SAChB;KACF;IACD,MAAM,EAAE,CAAC,MAAM,CAAC;IAChB,YAAY,EAAE,CAAC,QAAa,EAAa,EAAE;QACzC,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClC,OAAO;YACL,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,IAAI,EAAE,YAAY;YAClB,QAAQ,EAAE,IAAI;YACd,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,KAAK;gBACrC,IAAI,EAAE,QAAQ,CAAC,IAAI;aACpB;SACF,CAAC;IACJ,CAAC;CACF,CAAC","sourcesContent":["import { html } from 'lit-html';\nimport { ActionConfig, ACTION_GROUPS, FlowTypes } from '../types';\nimport { Node, EnterFlow } from '../../store/flow-definition';\nimport { renderNamedObjects } from '../utils';\n\nexport const enter_flow: ActionConfig = {\n name: 'Enter a Flow',\n group: ACTION_GROUPS.trigger,\n hideFromActions: true,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n render: (_node: Node, action: EnterFlow) => {\n return html`${renderNamedObjects([action.flow], 'flow')}`;\n },\n toFormData: (action: EnterFlow) => {\n return {\n uuid: action.uuid,\n flow: action.flow ? [action.flow] : []\n };\n },\n form: {\n flow: {\n type: 'select',\n required: true,\n placeholder: 'Select a flow...',\n helpText: 'The contact will enter this flow and not return',\n endpoint: '/api/v2/flows.json',\n valueKey: 'uuid',\n nameKey: 'name'\n }\n },\n layout: ['flow'],\n fromFormData: (formData: any): EnterFlow => {\n const selected = formData.flow[0];\n return {\n uuid: formData.uuid,\n type: 'enter_flow',\n terminal: true,\n flow: {\n uuid: selected.uuid || selected.value,\n name: selected.name\n }\n };\n }\n};\n"]}
1
+ {"version":3,"file":"enter_flow.js","sourceRoot":"","sources":["../../../../src/flow/actions/enter_flow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAgB,aAAa,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAElE,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,CAAC,MAAM,UAAU,GAAiB;IACtC,IAAI,EAAE,cAAc;IACpB,KAAK,EAAE,aAAa,CAAC,OAAO;IAC5B,eAAe,EAAE,IAAI;IACrB,SAAS,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,UAAU,CAAC;IACrE,MAAM,EAAE,CAAC,KAAW,EAAE,MAAiB,EAAE,EAAE;QACzC,OAAO,IAAI,CAAA,GAAG,eAAe,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC;IACzD,CAAC;IACD,UAAU,EAAE,CAAC,MAAiB,EAAE,EAAE;QAChC,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE;SACvC,CAAC;IACJ,CAAC;IACD,IAAI,EAAE;QACJ,IAAI,EAAE;YACJ,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,IAAI;YACd,WAAW,EAAE,kBAAkB;YAC/B,QAAQ,EAAE,iDAAiD;YAC3D,QAAQ,EAAE,oBAAoB;YAC9B,QAAQ,EAAE,MAAM;YAChB,OAAO,EAAE,MAAM;SAChB;KACF;IACD,MAAM,EAAE,CAAC,MAAM,CAAC;IAChB,YAAY,EAAE,CAAC,QAAa,EAAa,EAAE;QACzC,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClC,OAAO;YACL,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,IAAI,EAAE,YAAY;YAClB,QAAQ,EAAE,IAAI;YACd,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,KAAK;gBACrC,IAAI,EAAE,QAAQ,CAAC,IAAI;aACpB;SACF,CAAC;IACJ,CAAC;CACF,CAAC","sourcesContent":["import { html } from 'lit-html';\nimport { ActionConfig, ACTION_GROUPS, FlowTypes } from '../types';\nimport { Node, EnterFlow } from '../../store/flow-definition';\nimport { renderFlowLinks } from '../utils';\n\nexport const enter_flow: ActionConfig = {\n name: 'Enter a Flow',\n group: ACTION_GROUPS.trigger,\n hideFromActions: true,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n render: (_node: Node, action: EnterFlow) => {\n return html`${renderFlowLinks([action.flow], 'flow')}`;\n },\n toFormData: (action: EnterFlow) => {\n return {\n uuid: action.uuid,\n flow: action.flow ? [action.flow] : []\n };\n },\n form: {\n flow: {\n type: 'select',\n required: true,\n placeholder: 'Select a flow...',\n helpText: 'The contact will enter this flow and not return',\n endpoint: '/api/v2/flows.json',\n valueKey: 'uuid',\n nameKey: 'name'\n }\n },\n layout: ['flow'],\n fromFormData: (formData: any): EnterFlow => {\n const selected = formData.flow[0];\n return {\n uuid: formData.uuid,\n type: 'enter_flow',\n terminal: true,\n flow: {\n uuid: selected.uuid || selected.value,\n name: selected.name\n }\n };\n }\n};\n"]}
@@ -2,6 +2,7 @@ import { html } from 'lit-html';
2
2
  import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
3
3
  import { ACTION_GROUPS, FlowTypes } from '../types';
4
4
  import { renderAudioPlayer } from './audio-player';
5
+ import { renderClamped } from '../utils';
5
6
  export const say_msg = {
6
7
  name: 'Say Message',
7
8
  group: ACTION_GROUPS.send,
@@ -9,7 +10,7 @@ export const say_msg = {
9
10
  render: (_node, action) => {
10
11
  const text = (action.text || '').replace(/\n/g, '<br>');
11
12
  return html `
12
- ${unsafeHTML(text)}
13
+ ${renderClamped(html `${unsafeHTML(text)}`, action.text || '')}
13
14
  ${action.audio_url
14
15
  ? html `<div style="margin-top: 0.5em;">
15
16
  ${renderAudioPlayer(action.audio_url)}
@@ -1 +1 @@
1
- {"version":3,"file":"say_msg.js","sourceRoot":"","sources":["../../../../src/flow/actions/say_msg.ts"],"names":[],"mappings":"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","sourcesContent":["import { html } from 'lit-html';\nimport { unsafeHTML } from 'lit-html/directives/unsafe-html.js';\nimport { ActionConfig, ACTION_GROUPS, FormData, FlowTypes } from '../types';\nimport { Node, SayMsg } from '../../store/flow-definition';\nimport { renderAudioPlayer } from './audio-player';\n\nexport const say_msg: ActionConfig = {\n name: 'Say Message',\n group: ACTION_GROUPS.send,\n flowTypes: [FlowTypes.VOICE],\n render: (_node: Node, action: SayMsg) => {\n const text = (action.text || '').replace(/\\n/g, '<br>');\n return html`\n ${unsafeHTML(text)}\n ${action.audio_url\n ? html`<div style=\"margin-top: 0.5em;\">\n ${renderAudioPlayer(action.audio_url)}\n </div>`\n : null}\n `;\n },\n form: {\n text: {\n type: 'textarea',\n label: 'Message',\n required: true,\n evaluated: true,\n placeholder: 'Enter message to speak...',\n minHeight: 80\n },\n audio_url: {\n type: 'media',\n label: 'Recording',\n required: false,\n accept: 'audio/*',\n optionalLink: 'Add a recording'\n }\n },\n layout: ['text', 'audio_url'],\n toFormData: (action: SayMsg) => {\n return {\n uuid: action.uuid,\n text: action.text || '',\n audio_url: action.audio_url || ''\n };\n },\n fromFormData: (data: FormData) => {\n const result: any = {\n uuid: data.uuid,\n type: 'say_msg',\n text: data.text || ''\n };\n if (data.audio_url && data.audio_url.trim() !== '') {\n result.audio_url = data.audio_url.trim();\n }\n return result as SayMsg;\n },\n sanitize: (formData: FormData): void => {\n if (formData.text && typeof formData.text === 'string') {\n formData.text = formData.text.trim();\n }\n },\n localizable: ['text', 'audio_url'],\n toLocalizationFormData: (\n action: SayMsg,\n localization: Record<string, any>\n ) => {\n const formData: FormData = {\n uuid: action.uuid\n };\n\n if (localization.text && Array.isArray(localization.text)) {\n formData.text = localization.text[0] || '';\n } else {\n formData.text = '';\n }\n\n if (localization.audio_url && Array.isArray(localization.audio_url)) {\n formData.audio_url = localization.audio_url[0] || '';\n } else {\n formData.audio_url = '';\n }\n\n return formData;\n },\n fromLocalizationFormData: (formData: FormData, action: SayMsg) => {\n const localization: Record<string, any> = {};\n\n if (formData.text && formData.text.trim() !== '') {\n if (formData.text !== action.text) {\n localization.text = [formData.text];\n }\n }\n\n if (formData.audio_url && formData.audio_url.trim() !== '') {\n if (formData.audio_url !== action.audio_url) {\n localization.audio_url = [formData.audio_url];\n }\n }\n\n return localization;\n }\n};\n"]}
1
+ {"version":3,"file":"say_msg.js","sourceRoot":"","sources":["../../../../src/flow/actions/say_msg.ts"],"names":[],"mappings":"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","sourcesContent":["import { html } from 'lit-html';\nimport { unsafeHTML } from 'lit-html/directives/unsafe-html.js';\nimport { ActionConfig, ACTION_GROUPS, FormData, FlowTypes } from '../types';\nimport { Node, SayMsg } from '../../store/flow-definition';\nimport { renderAudioPlayer } from './audio-player';\nimport { renderClamped } from '../utils';\n\nexport const say_msg: ActionConfig = {\n name: 'Say Message',\n group: ACTION_GROUPS.send,\n flowTypes: [FlowTypes.VOICE],\n render: (_node: Node, action: SayMsg) => {\n const text = (action.text || '').replace(/\\n/g, '<br>');\n return html`\n ${renderClamped(html`${unsafeHTML(text)}`, action.text || '')}\n ${action.audio_url\n ? html`<div style=\"margin-top: 0.5em;\">\n ${renderAudioPlayer(action.audio_url)}\n </div>`\n : null}\n `;\n },\n form: {\n text: {\n type: 'textarea',\n label: 'Message',\n required: true,\n evaluated: true,\n placeholder: 'Enter message to speak...',\n minHeight: 80\n },\n audio_url: {\n type: 'media',\n label: 'Recording',\n required: false,\n accept: 'audio/*',\n optionalLink: 'Add a recording'\n }\n },\n layout: ['text', 'audio_url'],\n toFormData: (action: SayMsg) => {\n return {\n uuid: action.uuid,\n text: action.text || '',\n audio_url: action.audio_url || ''\n };\n },\n fromFormData: (data: FormData) => {\n const result: any = {\n uuid: data.uuid,\n type: 'say_msg',\n text: data.text || ''\n };\n if (data.audio_url && data.audio_url.trim() !== '') {\n result.audio_url = data.audio_url.trim();\n }\n return result as SayMsg;\n },\n sanitize: (formData: FormData): void => {\n if (formData.text && typeof formData.text === 'string') {\n formData.text = formData.text.trim();\n }\n },\n localizable: ['text', 'audio_url'],\n toLocalizationFormData: (\n action: SayMsg,\n localization: Record<string, any>\n ) => {\n const formData: FormData = {\n uuid: action.uuid\n };\n\n if (localization.text && Array.isArray(localization.text)) {\n formData.text = localization.text[0] || '';\n } else {\n formData.text = '';\n }\n\n if (localization.audio_url && Array.isArray(localization.audio_url)) {\n formData.audio_url = localization.audio_url[0] || '';\n } else {\n formData.audio_url = '';\n }\n\n return formData;\n },\n fromLocalizationFormData: (formData: FormData, action: SayMsg) => {\n const localization: Record<string, any> = {};\n\n if (formData.text && formData.text.trim() !== '') {\n if (formData.text !== action.text) {\n localization.text = [formData.text];\n }\n }\n\n if (formData.audio_url && formData.audio_url.trim() !== '') {\n if (formData.audio_url !== action.audio_url) {\n localization.audio_url = [formData.audio_url];\n }\n }\n\n return localization;\n }\n};\n"]}
@@ -1,6 +1,6 @@
1
1
  import { html } from 'lit-html';
2
2
  import { ACTION_GROUPS, FlowTypes } from '../types';
3
- import { renderStringList } from '../utils';
3
+ import { renderStringList, renderClamped } from '../utils';
4
4
  import { Icon } from '../../Icons';
5
5
  export const send_broadcast = {
6
6
  name: 'Send Broadcast',
@@ -14,11 +14,7 @@ export const send_broadcast = {
14
14
  return html `<div>
15
15
  <div>${renderStringList(recipients, Icon.contacts)}</div>
16
16
  <div style="margin-top: 0.5em">
17
- <div
18
- style="word-wrap: break-word; overflow-wrap: break-word; hyphens: auto;"
19
- >
20
- ${action.text}
21
- </div>
17
+ ${renderClamped(action.text, action.text)}
22
18
  </div>
23
19
  </div>`;
24
20
  },
@@ -1 +1 @@
1
- {"version":3,"file":"send_broadcast.js","sourceRoot":"","sources":["../../../../src/flow/actions/send_broadcast.ts"],"names":[],"mappings":"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","sourcesContent":["import { html } from 'lit-html';\nimport { ActionConfig, ACTION_GROUPS, FormData, FlowTypes } from '../types';\nimport { Node, SendBroadcast } from '../../store/flow-definition';\nimport { renderStringList } from '../utils';\nimport { Icon } from '../../Icons';\n\nexport const send_broadcast: ActionConfig = {\n name: 'Send Broadcast',\n group: ACTION_GROUPS.broadcast,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n render: (_node: Node, action: SendBroadcast) => {\n const recipients = [\n ...(action.contacts || []).map((c) => c.name),\n ...(action.groups || []).map((g) => g.name)\n ];\n\n return html`<div>\n <div>${renderStringList(recipients, Icon.contacts)}</div>\n <div style=\"margin-top: 0.5em\">\n <div\n style=\"word-wrap: break-word; overflow-wrap: break-word; hyphens: auto;\"\n >\n ${action.text}\n </div>\n </div>\n </div>`;\n },\n\n form: {\n recipients: {\n type: 'select',\n label: 'Recipients',\n helpText: 'Select the contacts or groups to receive the broadcast',\n options: [],\n multi: true,\n searchable: true,\n endpoint: '/api/v2/contacts.json',\n valueKey: 'uuid',\n nameKey: 'name',\n placeholder: 'Search for contacts or groups...',\n required: true\n },\n text: {\n type: 'message-editor',\n label: 'Message',\n helpText:\n 'Enter the message to send with optional attachments. You can use expressions like @contact.name',\n required: true,\n evaluated: true,\n placeholder: 'Type your message here...',\n maxAttachments: 10,\n accept: '',\n endpoint: '/api/v2/media.json',\n counter: 'temba-charcount',\n gsm: true,\n autogrow: true\n }\n },\n\n layout: ['recipients', 'text'],\n\n toFormData: (action: SendBroadcast) => {\n return {\n uuid: action.uuid,\n recipients: [...(action.contacts || []), ...(action.groups || [])],\n text: action.text || '',\n attachments: action.attachments || []\n };\n },\n\n fromFormData: (formData: FormData): SendBroadcast => {\n const recipients = formData.recipients || [];\n const contacts = recipients\n .filter((r: any) => !r.group)\n .map((c: any) => ({ uuid: c.uuid, name: c.name }));\n const groups = recipients\n .filter((r: any) => r.group)\n .map((g: any) => ({ uuid: g.uuid, name: g.name }));\n\n const result: SendBroadcast = {\n uuid: formData.uuid,\n type: 'send_broadcast',\n text: formData.text || '',\n contacts,\n groups,\n attachments: formData.attachments || []\n };\n\n // Remove empty attachments array to match original format\n if (result.attachments.length === 0) {\n delete result.attachments;\n }\n\n return result;\n },\n\n sanitize: (formData: FormData): void => {\n if (formData.text && typeof formData.text === 'string') {\n formData.text = formData.text.trim();\n }\n }\n};\n"]}
1
+ {"version":3,"file":"send_broadcast.js","sourceRoot":"","sources":["../../../../src/flow/actions/send_broadcast.ts"],"names":[],"mappings":"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","sourcesContent":["import { html } from 'lit-html';\nimport { ActionConfig, ACTION_GROUPS, FormData, FlowTypes } from '../types';\nimport { Node, SendBroadcast } from '../../store/flow-definition';\nimport { renderStringList, renderClamped } from '../utils';\nimport { Icon } from '../../Icons';\n\nexport const send_broadcast: ActionConfig = {\n name: 'Send Broadcast',\n group: ACTION_GROUPS.broadcast,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n render: (_node: Node, action: SendBroadcast) => {\n const recipients = [\n ...(action.contacts || []).map((c) => c.name),\n ...(action.groups || []).map((g) => g.name)\n ];\n\n return html`<div>\n <div>${renderStringList(recipients, Icon.contacts)}</div>\n <div style=\"margin-top: 0.5em\">\n ${renderClamped(action.text, action.text)}\n </div>\n </div>`;\n },\n\n form: {\n recipients: {\n type: 'select',\n label: 'Recipients',\n helpText: 'Select the contacts or groups to receive the broadcast',\n options: [],\n multi: true,\n searchable: true,\n endpoint: '/api/v2/contacts.json',\n valueKey: 'uuid',\n nameKey: 'name',\n placeholder: 'Search for contacts or groups...',\n required: true\n },\n text: {\n type: 'message-editor',\n label: 'Message',\n helpText:\n 'Enter the message to send with optional attachments. You can use expressions like @contact.name',\n required: true,\n evaluated: true,\n placeholder: 'Type your message here...',\n maxAttachments: 10,\n accept: '',\n endpoint: '/api/v2/media.json',\n counter: 'temba-charcount',\n gsm: true,\n autogrow: true\n }\n },\n\n layout: ['recipients', 'text'],\n\n toFormData: (action: SendBroadcast) => {\n return {\n uuid: action.uuid,\n recipients: [...(action.contacts || []), ...(action.groups || [])],\n text: action.text || '',\n attachments: action.attachments || []\n };\n },\n\n fromFormData: (formData: FormData): SendBroadcast => {\n const recipients = formData.recipients || [];\n const contacts = recipients\n .filter((r: any) => !r.group)\n .map((c: any) => ({ uuid: c.uuid, name: c.name }));\n const groups = recipients\n .filter((r: any) => r.group)\n .map((g: any) => ({ uuid: g.uuid, name: g.name }));\n\n const result: SendBroadcast = {\n uuid: formData.uuid,\n type: 'send_broadcast',\n text: formData.text || '',\n contacts,\n groups,\n attachments: formData.attachments || []\n };\n\n // Remove empty attachments array to match original format\n if (result.attachments.length === 0) {\n delete result.attachments;\n }\n\n return result;\n },\n\n sanitize: (formData: FormData): void => {\n if (formData.text && typeof formData.text === 'string') {\n formData.text = formData.text.trim();\n }\n }\n};\n"]}
@@ -1,6 +1,6 @@
1
1
  import { html } from 'lit-html';
2
2
  import { ACTION_GROUPS, FlowTypes } from '../types';
3
- import { renderStringList } from '../utils';
3
+ import { renderStringList, renderClamped } from '../utils';
4
4
  import { Icon } from '../../Icons';
5
5
  export const send_email = {
6
6
  name: 'Send Email',
@@ -10,11 +10,7 @@ export const send_email = {
10
10
  return html `<div>
11
11
  <div>${renderStringList(action.addresses, Icon.email)}</div>
12
12
  <div style="margin-top: 0.5em">
13
- <div
14
- style="word-wrap: break-word; overflow-wrap: break-word; hyphens: auto;"
15
- >
16
- ${action.subject}
17
- </div>
13
+ ${renderClamped(action.subject, action.subject)}
18
14
  </div>
19
15
  </div>`;
20
16
  },
@@ -1 +1 @@
1
- {"version":3,"file":"send_email.js","sourceRoot":"","sources":["../../../../src/flow/actions/send_email.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAEL,aAAa,EAGb,SAAS,EACV,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnC,MAAM,CAAC,MAAM,UAAU,GAAiB;IACtC,IAAI,EAAE,YAAY;IAClB,KAAK,EAAE,aAAa,CAAC,SAAS;IAC9B,SAAS,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,UAAU,CAAC;IACrE,MAAM,EAAE,CAAC,KAAW,EAAE,MAAiB,EAAE,EAAE;QACzC,OAAO,IAAI,CAAA;aACF,gBAAgB,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC;;;;;YAK/C,MAAM,CAAC,OAAO;;;WAGf,CAAC;IACV,CAAC;IACD,IAAI,EAAE;QACJ,SAAS,EAAE;YACT,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,YAAY;YACnB,KAAK,EAAE,IAAI;YACX,UAAU,EAAE,IAAI;YAChB,WAAW,EAAE,wBAAwB;YACrC,MAAM,EAAE,IAAI;SACb;QACD,OAAO,EAAE;YACP,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,SAAS;YAChB,QAAQ,EAAE,IAAI;YACd,WAAW,EAAE,qBAAqB;YAClC,SAAS,EAAE,GAAG;SACf;QACD,IAAI,EAAE;YACJ,IAAI,EAAE,UAAU;YAChB,QAAQ,EAAE,IAAI;YACd,SAAS,EAAE,IAAI;YACf,SAAS,EAAE,GAAG;SACf;KACF;IACD,YAAY,EAAE,CAAC,QAAkB,EAAa,EAAE;QAC9C,OAAO;YACL,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,IAAI,EAAE,YAAY;YAClB,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,GAAG,CAC/B,CAAC,IAAqC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CACtD;YACD,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,IAAI,EAAE,QAAQ,CAAC,IAAI;SACpB,CAAC;IACJ,CAAC;IACD,QAAQ,EAAE,CAAC,QAAkB,EAAoB,EAAE;QACjD,MAAM,MAAM,GAA8B,EAAE,CAAC;QAE7C,IAAI,CAAC,QAAQ,CAAC,SAAS,IAAI,QAAQ,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3D,MAAM,CAAC,SAAS,GAAG,kDAAkD,CAAC;QACxE,CAAC;QAED,OAAO;YACL,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC;YACvC,MAAM;SACP,CAAC;IACJ,CAAC;CACF,CAAC","sourcesContent":["import { html } from 'lit-html';\nimport {\n ActionConfig,\n ACTION_GROUPS,\n FormData,\n ValidationResult,\n FlowTypes\n} from '../types';\nimport { Node, SendEmail } from '../../store/flow-definition';\nimport { renderStringList } from '../utils';\nimport { Icon } from '../../Icons';\n\nexport const send_email: ActionConfig = {\n name: 'Send Email',\n group: ACTION_GROUPS.broadcast,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n render: (_node: Node, action: SendEmail) => {\n return html`<div>\n <div>${renderStringList(action.addresses, Icon.email)}</div>\n <div style=\"margin-top: 0.5em\">\n <div\n style=\"word-wrap: break-word; overflow-wrap: break-word; hyphens: auto;\"\n >\n ${action.subject}\n </div>\n </div>\n </div>`;\n },\n form: {\n addresses: {\n type: 'select',\n label: 'Recipients',\n multi: true,\n searchable: true,\n placeholder: 'Search for contacts...',\n emails: true\n },\n subject: {\n type: 'text',\n label: 'Subject',\n required: true,\n placeholder: 'Enter email subject',\n maxLength: 255\n },\n body: {\n type: 'textarea',\n required: true,\n evaluated: true,\n minHeight: 175\n }\n },\n fromFormData: (formData: FormData): SendEmail => {\n return {\n uuid: formData.uuid,\n type: 'send_email',\n addresses: formData.addresses.map(\n (addr: { name: string; value: string }) => addr.value\n ),\n subject: formData.subject,\n body: formData.body\n };\n },\n validate: (formData: FormData): ValidationResult => {\n const errors: { [key: string]: string } = {};\n\n if (!formData.addresses || formData.addresses.length === 0) {\n errors.addresses = 'At least one recipient email address is required';\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors\n };\n }\n};\n"]}
1
+ {"version":3,"file":"send_email.js","sourceRoot":"","sources":["../../../../src/flow/actions/send_email.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAEL,aAAa,EAGb,SAAS,EACV,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnC,MAAM,CAAC,MAAM,UAAU,GAAiB;IACtC,IAAI,EAAE,YAAY;IAClB,KAAK,EAAE,aAAa,CAAC,SAAS;IAC9B,SAAS,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,UAAU,CAAC;IACrE,MAAM,EAAE,CAAC,KAAW,EAAE,MAAiB,EAAE,EAAE;QACzC,OAAO,IAAI,CAAA;aACF,gBAAgB,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC;;UAEjD,aAAa,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC;;WAE5C,CAAC;IACV,CAAC;IACD,IAAI,EAAE;QACJ,SAAS,EAAE;YACT,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,YAAY;YACnB,KAAK,EAAE,IAAI;YACX,UAAU,EAAE,IAAI;YAChB,WAAW,EAAE,wBAAwB;YACrC,MAAM,EAAE,IAAI;SACb;QACD,OAAO,EAAE;YACP,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,SAAS;YAChB,QAAQ,EAAE,IAAI;YACd,WAAW,EAAE,qBAAqB;YAClC,SAAS,EAAE,GAAG;SACf;QACD,IAAI,EAAE;YACJ,IAAI,EAAE,UAAU;YAChB,QAAQ,EAAE,IAAI;YACd,SAAS,EAAE,IAAI;YACf,SAAS,EAAE,GAAG;SACf;KACF;IACD,YAAY,EAAE,CAAC,QAAkB,EAAa,EAAE;QAC9C,OAAO;YACL,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,IAAI,EAAE,YAAY;YAClB,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,GAAG,CAC/B,CAAC,IAAqC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CACtD;YACD,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,IAAI,EAAE,QAAQ,CAAC,IAAI;SACpB,CAAC;IACJ,CAAC;IACD,QAAQ,EAAE,CAAC,QAAkB,EAAoB,EAAE;QACjD,MAAM,MAAM,GAA8B,EAAE,CAAC;QAE7C,IAAI,CAAC,QAAQ,CAAC,SAAS,IAAI,QAAQ,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3D,MAAM,CAAC,SAAS,GAAG,kDAAkD,CAAC;QACxE,CAAC;QAED,OAAO;YACL,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC;YACvC,MAAM;SACP,CAAC;IACJ,CAAC;CACF,CAAC","sourcesContent":["import { html } from 'lit-html';\nimport {\n ActionConfig,\n ACTION_GROUPS,\n FormData,\n ValidationResult,\n FlowTypes\n} from '../types';\nimport { Node, SendEmail } from '../../store/flow-definition';\nimport { renderStringList, renderClamped } from '../utils';\nimport { Icon } from '../../Icons';\n\nexport const send_email: ActionConfig = {\n name: 'Send Email',\n group: ACTION_GROUPS.broadcast,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n render: (_node: Node, action: SendEmail) => {\n return html`<div>\n <div>${renderStringList(action.addresses, Icon.email)}</div>\n <div style=\"margin-top: 0.5em\">\n ${renderClamped(action.subject, action.subject)}\n </div>\n </div>`;\n },\n form: {\n addresses: {\n type: 'select',\n label: 'Recipients',\n multi: true,\n searchable: true,\n placeholder: 'Search for contacts...',\n emails: true\n },\n subject: {\n type: 'text',\n label: 'Subject',\n required: true,\n placeholder: 'Enter email subject',\n maxLength: 255\n },\n body: {\n type: 'textarea',\n required: true,\n evaluated: true,\n minHeight: 175\n }\n },\n fromFormData: (formData: FormData): SendEmail => {\n return {\n uuid: formData.uuid,\n type: 'send_email',\n addresses: formData.addresses.map(\n (addr: { name: string; value: string }) => addr.value\n ),\n subject: formData.subject,\n body: formData.body\n };\n },\n validate: (formData: FormData): ValidationResult => {\n const errors: { [key: string]: string } = {};\n\n if (!formData.addresses || formData.addresses.length === 0) {\n errors.addresses = 'At least one recipient email address is required';\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors\n };\n }\n};\n"]}
@@ -2,28 +2,30 @@ import { html } from 'lit-html';
2
2
  import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
3
3
  import { ACTION_GROUPS, FlowTypes } from '../types';
4
4
  import { titleCase } from '../../utils';
5
+ import { renderClamped } from '../utils';
5
6
  export const send_msg = {
6
7
  name: 'Send Message',
7
8
  group: ACTION_GROUPS.send,
8
9
  flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],
10
+ hideFromActions: true,
9
11
  render: (_node, action) => {
10
12
  var _a;
11
13
  const text = action.text.replace(/\n/g, '<br>');
12
14
  return html `
13
- ${unsafeHTML(text)}
15
+ ${renderClamped(html `${unsafeHTML(text)}`, action.text)}
14
16
  ${((_a = (action.quick_replies || [])) === null || _a === void 0 ? void 0 : _a.length) > 0
15
17
  ? html `<div class="quick-replies">
16
18
  ${(action.quick_replies || []).map((reply) => {
17
19
  return html `<div class="quick-reply">${reply}</div>`;
18
20
  })}
19
- ${action.template
20
- ? html `<div
21
- style="border: 1px solid var(--color-widget-border);padding: 0.5em;margin-top: 1em;border-radius: var(--curvature); display:flex;background: rgba(0,0,0,.03);"
22
- >
23
- <temba-icon name="channel_wac"></temba-icon>
24
- <div style="margin-left:0.5em">${action.template.name}</div>
25
- </div>`
26
- : null}
21
+ </div>`
22
+ : null}
23
+ ${action.template
24
+ ? html `<div
25
+ style="border: 1px solid var(--color-widget-border);padding: 0.5em;margin-top: 1em;border-radius: var(--curvature); display:flex;background: rgba(0,0,0,.03);"
26
+ >
27
+ <temba-icon name="channel_wac"></temba-icon>
28
+ <div style="margin-left:0.5em">${action.template.name}</div>
27
29
  </div>`
28
30
  : null}
29
31
  `;
@@ -53,6 +55,10 @@ export const send_msg = {
53
55
  maxItems: 10,
54
56
  evaluated: true
55
57
  },
58
+ template: {
59
+ type: 'template-editor',
60
+ endpoint: '/api/internal/templates.json'
61
+ },
56
62
  runtime_attachments: {
57
63
  type: 'array',
58
64
  itemLabel: 'Attachment',
@@ -86,31 +92,35 @@ export const send_msg = {
86
92
  layout: [
87
93
  'text',
88
94
  {
89
- type: 'group',
90
- label: 'Quick Replies',
91
- items: ['quick_replies'],
92
- collapsible: true,
93
- collapsed: (formData) => {
94
- // Collapse only if there are no quick replies
95
- return !formData.quick_replies || formData.quick_replies.length === 0;
96
- },
97
- getGroupValueCount: (formData) => {
98
- var _a;
99
- return ((_a = formData.quick_replies) === null || _a === void 0 ? void 0 : _a.length) || 0;
100
- }
101
- },
102
- {
103
- type: 'group',
104
- label: 'Runtime Attachments',
105
- items: ['runtime_attachments'],
106
- collapsible: true,
107
- collapsed: true,
108
- helpText: 'Add dynamic attachments that are evaluated at runtime',
109
- contentPadding: '12px',
110
- getGroupValueCount: (formData) => {
111
- var _a;
112
- return (((_a = formData.runtime_attachments) === null || _a === void 0 ? void 0 : _a.filter((item) => item && item.expression && item.expression.trim() !== '').length) || 0);
113
- }
95
+ type: 'accordion',
96
+ sections: [
97
+ {
98
+ label: 'Quick Replies',
99
+ collapsed: true,
100
+ getValueCount: (formData) => {
101
+ var _a;
102
+ return ((_a = formData.quick_replies) === null || _a === void 0 ? void 0 : _a.length) || 0;
103
+ },
104
+ items: ['quick_replies']
105
+ },
106
+ {
107
+ label: 'WhatsApp Template',
108
+ collapsed: true,
109
+ getValueCount: (formData) => {
110
+ return !!formData.template;
111
+ },
112
+ items: ['template']
113
+ },
114
+ {
115
+ label: 'Runtime Attachments',
116
+ collapsed: true,
117
+ getValueCount: (formData) => {
118
+ var _a;
119
+ return (((_a = formData.runtime_attachments) === null || _a === void 0 ? void 0 : _a.filter((item) => item && item.expression && item.expression.trim() !== '').length) || 0);
120
+ },
121
+ items: ['runtime_attachments']
122
+ }
123
+ ]
114
124
  }
115
125
  ],
116
126
  toFormData: (action) => {
@@ -143,7 +153,9 @@ export const send_msg = {
143
153
  quick_replies: (action.quick_replies || []).map((reply) => ({
144
154
  name: reply,
145
155
  value: reply
146
- }))
156
+ })),
157
+ template: action.template || null,
158
+ template_variables: action.template_variables || []
147
159
  };
148
160
  },
149
161
  fromFormData: (data) => {
@@ -164,6 +176,11 @@ export const send_msg = {
164
176
  if (result.quick_replies.length === 0) {
165
177
  delete result.quick_replies;
166
178
  }
179
+ // Add template and template_variables if a template is selected
180
+ if (data.template) {
181
+ result.template = data.template;
182
+ result.template_variables = data.template_variables || [];
183
+ }
167
184
  return result;
168
185
  },
169
186
  sanitize: (formData) => {
@@ -1 +1 @@
1
- {"version":3,"file":"send_msg.js","sourceRoot":"","sources":["../../../../src/flow/actions/send_msg.ts"],"names":[],"mappings":"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","sourcesContent":["import { html } from 'lit-html';\nimport { unsafeHTML } from 'lit-html/directives/unsafe-html.js';\nimport {\n ActionConfig,\n ACTION_GROUPS,\n FormData,\n ValidationResult,\n FlowTypes\n} from '../types';\nimport { Node, SendMsg } from '../../store/flow-definition';\nimport { titleCase } from '../../utils';\n\nexport const send_msg: ActionConfig = {\n name: 'Send Message',\n group: ACTION_GROUPS.send,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n render: (_node: Node, action: SendMsg) => {\n const text = action.text.replace(/\\n/g, '<br>');\n return html`\n ${unsafeHTML(text)}\n ${(action.quick_replies || [])?.length > 0\n ? html`<div class=\"quick-replies\">\n ${(action.quick_replies || []).map((reply) => {\n return html`<div class=\"quick-reply\">${reply}</div>`;\n })}\n ${action.template\n ? html`<div\n style=\"border: 1px solid var(--color-widget-border);padding: 0.5em;margin-top: 1em;border-radius: var(--curvature); display:flex;background: rgba(0,0,0,.03);\"\n >\n <temba-icon name=\"channel_wac\"></temba-icon>\n <div style=\"margin-left:0.5em\">${action.template.name}</div>\n </div>`\n : null}\n </div>`\n : null}\n `;\n },\n form: {\n text: {\n type: 'message-editor',\n label: 'Message',\n helpText:\n 'Enter the message to send with optional attachments. You can use expressions like @contact.name',\n required: true,\n evaluated: true,\n placeholder: 'Type your message here...',\n maxAttachments: 10,\n accept: '',\n endpoint: '/api/v2/media.json',\n counter: 'temba-charcount',\n gsm: true,\n autogrow: true\n },\n quick_replies: {\n type: 'select',\n options: [],\n multi: true,\n tags: true,\n searchable: true,\n placeholder: 'Add quick replies...',\n maxItems: 10,\n evaluated: true\n },\n runtime_attachments: {\n type: 'array',\n itemLabel: 'Attachment',\n sortable: true,\n maxItems: 10,\n isEmptyItem: (item: any) => {\n return !item.expression || item.expression.trim() === '';\n },\n itemConfig: {\n type: {\n type: 'select',\n width: '140px',\n options: [\n { value: 'image', name: 'Image' },\n { value: 'audio', name: 'Audio' },\n { value: 'video', name: 'Video' },\n { value: 'document', name: 'Document' }\n ],\n required: true,\n searchable: false\n },\n expression: {\n type: 'text',\n placeholder: 'Expression (e.g. @contact.photo)',\n required: true,\n evaluated: true\n }\n }\n }\n },\n layout: [\n 'text',\n {\n type: 'group',\n label: 'Quick Replies',\n items: ['quick_replies'],\n collapsible: true,\n collapsed: (formData: FormData) => {\n // Collapse only if there are no quick replies\n return !formData.quick_replies || formData.quick_replies.length === 0;\n },\n getGroupValueCount: (formData: FormData) => {\n return formData.quick_replies?.length || 0;\n }\n },\n {\n type: 'group',\n label: 'Runtime Attachments',\n items: ['runtime_attachments'],\n collapsible: true,\n collapsed: true,\n helpText: 'Add dynamic attachments that are evaluated at runtime',\n contentPadding: '12px',\n getGroupValueCount: (formData: FormData) => {\n return (\n formData.runtime_attachments?.filter(\n (item: any) =>\n item && item.expression && item.expression.trim() !== ''\n ).length || 0\n );\n }\n }\n ],\n toFormData: (action: SendMsg) => {\n // Extract runtime attachments from the text field attachments\n const runtimeAttachments: {\n type: { name: string; value: string };\n expression: string;\n }[] = [];\n const staticAttachments: string[] = [];\n\n if (action.attachments && Array.isArray(action.attachments)) {\n action.attachments.forEach((attachment) => {\n if (typeof attachment === 'string' && attachment.includes(':')) {\n const colonIndex = attachment.indexOf(':');\n const contentType = attachment.substring(0, colonIndex);\n const value = attachment.substring(colonIndex + 1);\n\n if (!contentType.includes('/')) {\n runtimeAttachments.push({\n type: { name: titleCase(contentType), value: contentType },\n expression: value\n });\n } else {\n staticAttachments.push(attachment);\n }\n }\n });\n }\n\n return {\n uuid: action.uuid,\n text: action.text || '',\n attachments: staticAttachments,\n runtime_attachments: runtimeAttachments,\n quick_replies: (action.quick_replies || []).map((reply) => ({\n name: reply,\n value: reply\n }))\n };\n },\n fromFormData: (data: FormData) => {\n const result = {\n uuid: data.uuid,\n type: 'send_msg',\n text: data.text || '',\n attachments: [],\n quick_replies: (data.quick_replies || []).map((reply: any) =>\n typeof reply === 'string' ? reply : reply.value || reply.name || reply\n )\n };\n\n // Combine static attachments from text field with runtime attachments\n const staticAttachments = data.attachments || [];\n const runtimeAttachments = (data.runtime_attachments || [])\n .filter(\n (item: {\n type: [{ name: string; value: string }];\n expression: string;\n }) => item && item.type && item.expression\n ) // Filter out invalid items\n .map(\n (item: {\n type: [{ name: string; value: string }];\n expression: string;\n }) => `${item.type[0].value}:${item.expression}`\n );\n\n result.attachments = [...staticAttachments, ...runtimeAttachments];\n\n // Remove quick_replies if empty to match original format\n if (result.quick_replies.length === 0) {\n delete (result as any).quick_replies;\n }\n\n return result as SendMsg;\n },\n sanitize: (formData: FormData): void => {\n if (formData.text && typeof formData.text === 'string') {\n formData.text = formData.text.trim();\n }\n },\n validate: (formData: FormData): ValidationResult => {\n const errors: { [key: string]: string } = {};\n\n // Check total attachment count (static + runtime should not exceed 10)\n const staticAttachments = formData.attachments || [];\n const runtimeAttachments = (formData.runtime_attachments || []).filter(\n (item: any) => item && item.expression && item.expression.trim() !== ''\n );\n\n const totalAttachments =\n staticAttachments.length + runtimeAttachments.length;\n if (totalAttachments > 10) {\n if (runtimeAttachments.length > 0) {\n errors.runtime_attachments =\n 'Each message can only have up to 10 attachments';\n }\n if (staticAttachments.length > 0) {\n errors.text = 'Each message can only have up to 10 total attachments';\n }\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors\n };\n },\n localizable: ['text', 'quick_replies', 'attachments'],\n toLocalizationFormData: (\n action: SendMsg,\n localization: Record<string, any>\n ) => {\n // Convert localized values to form data format\n // Localized values are stored as arrays even for single values\n const formData: FormData = {\n uuid: action.uuid\n };\n\n // Handle text (single value, but stored as array in localization)\n if (localization.text && Array.isArray(localization.text)) {\n formData.text = localization.text[0] || '';\n } else {\n // Fall back to empty string if no localization\n formData.text = '';\n }\n\n // Handle attachments (already an array)\n if (localization.attachments && Array.isArray(localization.attachments)) {\n formData.attachments = localization.attachments;\n }\n\n // Handle quick_replies (already an array)\n if (\n localization.quick_replies &&\n Array.isArray(localization.quick_replies)\n ) {\n formData.quick_replies = localization.quick_replies.map((reply) => ({\n name: reply,\n value: reply\n }));\n }\n\n // Extract runtime attachments from localized attachments\n const runtimeAttachments: {\n type: { name: string; value: string };\n expression: string;\n }[] = [];\n const staticAttachments: string[] = [];\n\n if (formData.attachments && Array.isArray(formData.attachments)) {\n formData.attachments.forEach((attachment) => {\n if (typeof attachment === 'string' && attachment.includes(':')) {\n const colonIndex = attachment.indexOf(':');\n const contentType = attachment.substring(0, colonIndex);\n const value = attachment.substring(colonIndex + 1);\n\n if (!contentType.includes('/')) {\n runtimeAttachments.push({\n type: { name: titleCase(contentType), value: contentType },\n expression: value\n });\n } else {\n staticAttachments.push(attachment);\n }\n }\n });\n }\n\n formData.attachments = staticAttachments;\n formData.runtime_attachments = runtimeAttachments;\n\n return formData;\n },\n fromLocalizationFormData: (formData: FormData, action: SendMsg) => {\n // Convert form data to localization format\n // All values must be stored as arrays\n const localization: Record<string, any> = {};\n\n // Handle text (store as single-element array)\n // Only save if not empty and different from base action\n if (formData.text && formData.text.trim() !== '') {\n if (formData.text !== action.text) {\n localization.text = [formData.text];\n }\n }\n\n // Handle quick_replies (store as array)\n const quickReplies = (formData.quick_replies || [])\n .map((reply: any) =>\n typeof reply === 'string' ? reply : reply.value || reply.name || reply\n )\n .filter((reply: string) => reply && reply.trim() !== '');\n\n // Only save if there are quick replies and different from base action\n if (quickReplies.length > 0) {\n if (\n JSON.stringify(quickReplies) !==\n JSON.stringify(action.quick_replies || [])\n ) {\n localization.quick_replies = quickReplies;\n }\n }\n\n // Handle attachments (combine static and runtime attachments)\n const staticAttachments = (formData.attachments || []).filter(\n (att: string) => att && att.trim() !== ''\n );\n const runtimeAttachments = (formData.runtime_attachments || [])\n .filter(\n (item: {\n type: [{ name: string; value: string }];\n expression: string;\n }) =>\n item && item.type && item.expression && item.expression.trim() !== ''\n )\n .map(\n (item: {\n type: [{ name: string; value: string }];\n expression: string;\n }) => `${item.type[0].value}:${item.expression}`\n );\n\n const allAttachments = [...staticAttachments, ...runtimeAttachments];\n\n // Only save if there are attachments and different from base action\n if (allAttachments.length > 0) {\n if (\n JSON.stringify(allAttachments) !==\n JSON.stringify(action.attachments || [])\n ) {\n localization.attachments = allAttachments;\n }\n }\n\n return localization;\n }\n};\n"]}
1
+ {"version":3,"file":"send_msg.js","sourceRoot":"","sources":["../../../../src/flow/actions/send_msg.ts"],"names":[],"mappings":"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","sourcesContent":["import { html } from 'lit-html';\nimport { unsafeHTML } from 'lit-html/directives/unsafe-html.js';\nimport {\n ActionConfig,\n ACTION_GROUPS,\n FormData,\n ValidationResult,\n FlowTypes\n} from '../types';\nimport { Node, SendMsg } from '../../store/flow-definition';\nimport { titleCase } from '../../utils';\nimport { renderClamped } from '../utils';\n\nexport const send_msg: ActionConfig = {\n name: 'Send Message',\n group: ACTION_GROUPS.send,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n hideFromActions: true,\n render: (_node: Node, action: SendMsg) => {\n const text = action.text.replace(/\\n/g, '<br>');\n return html`\n ${renderClamped(html`${unsafeHTML(text)}`, action.text)}\n ${(action.quick_replies || [])?.length > 0\n ? html`<div class=\"quick-replies\">\n ${(action.quick_replies || []).map((reply) => {\n return html`<div class=\"quick-reply\">${reply}</div>`;\n })}\n </div>`\n : null}\n ${action.template\n ? html`<div\n style=\"border: 1px solid var(--color-widget-border);padding: 0.5em;margin-top: 1em;border-radius: var(--curvature); display:flex;background: rgba(0,0,0,.03);\"\n >\n <temba-icon name=\"channel_wac\"></temba-icon>\n <div style=\"margin-left:0.5em\">${action.template.name}</div>\n </div>`\n : null}\n `;\n },\n form: {\n text: {\n type: 'message-editor',\n label: 'Message',\n helpText:\n 'Enter the message to send with optional attachments. You can use expressions like @contact.name',\n required: true,\n evaluated: true,\n placeholder: 'Type your message here...',\n maxAttachments: 10,\n accept: '',\n endpoint: '/api/v2/media.json',\n counter: 'temba-charcount',\n gsm: true,\n autogrow: true\n },\n quick_replies: {\n type: 'select',\n options: [],\n multi: true,\n tags: true,\n searchable: true,\n placeholder: 'Add quick replies...',\n maxItems: 10,\n evaluated: true\n },\n template: {\n type: 'template-editor',\n endpoint: '/api/internal/templates.json'\n },\n runtime_attachments: {\n type: 'array',\n itemLabel: 'Attachment',\n sortable: true,\n maxItems: 10,\n isEmptyItem: (item: any) => {\n return !item.expression || item.expression.trim() === '';\n },\n itemConfig: {\n type: {\n type: 'select',\n width: '140px',\n options: [\n { value: 'image', name: 'Image' },\n { value: 'audio', name: 'Audio' },\n { value: 'video', name: 'Video' },\n { value: 'document', name: 'Document' }\n ],\n required: true,\n searchable: false\n },\n expression: {\n type: 'text',\n placeholder: 'Expression (e.g. @contact.photo)',\n required: true,\n evaluated: true\n }\n }\n }\n },\n layout: [\n 'text',\n {\n type: 'accordion',\n sections: [\n {\n label: 'Quick Replies',\n collapsed: true,\n getValueCount: (formData: FormData) => {\n return formData.quick_replies?.length || 0;\n },\n items: ['quick_replies']\n },\n {\n label: 'WhatsApp Template',\n collapsed: true,\n getValueCount: (formData: FormData) => {\n return !!formData.template;\n },\n items: ['template']\n },\n {\n label: 'Runtime Attachments',\n collapsed: true,\n getValueCount: (formData: FormData) => {\n return (\n formData.runtime_attachments?.filter(\n (item: any) =>\n item && item.expression && item.expression.trim() !== ''\n ).length || 0\n );\n },\n items: ['runtime_attachments']\n }\n ]\n }\n ],\n toFormData: (action: SendMsg) => {\n // Extract runtime attachments from the text field attachments\n const runtimeAttachments: {\n type: { name: string; value: string };\n expression: string;\n }[] = [];\n const staticAttachments: string[] = [];\n\n if (action.attachments && Array.isArray(action.attachments)) {\n action.attachments.forEach((attachment) => {\n if (typeof attachment === 'string' && attachment.includes(':')) {\n const colonIndex = attachment.indexOf(':');\n const contentType = attachment.substring(0, colonIndex);\n const value = attachment.substring(colonIndex + 1);\n\n if (!contentType.includes('/')) {\n runtimeAttachments.push({\n type: { name: titleCase(contentType), value: contentType },\n expression: value\n });\n } else {\n staticAttachments.push(attachment);\n }\n }\n });\n }\n\n return {\n uuid: action.uuid,\n text: action.text || '',\n attachments: staticAttachments,\n runtime_attachments: runtimeAttachments,\n quick_replies: (action.quick_replies || []).map((reply) => ({\n name: reply,\n value: reply\n })),\n template: action.template || null,\n template_variables: action.template_variables || []\n };\n },\n fromFormData: (data: FormData) => {\n const result = {\n uuid: data.uuid,\n type: 'send_msg',\n text: data.text || '',\n attachments: [],\n quick_replies: (data.quick_replies || []).map((reply: any) =>\n typeof reply === 'string' ? reply : reply.value || reply.name || reply\n )\n };\n\n // Combine static attachments from text field with runtime attachments\n const staticAttachments = data.attachments || [];\n const runtimeAttachments = (data.runtime_attachments || [])\n .filter(\n (item: {\n type: [{ name: string; value: string }];\n expression: string;\n }) => item && item.type && item.expression\n ) // Filter out invalid items\n .map(\n (item: {\n type: [{ name: string; value: string }];\n expression: string;\n }) => `${item.type[0].value}:${item.expression}`\n );\n\n result.attachments = [...staticAttachments, ...runtimeAttachments];\n\n // Remove quick_replies if empty to match original format\n if (result.quick_replies.length === 0) {\n delete (result as any).quick_replies;\n }\n\n // Add template and template_variables if a template is selected\n if (data.template) {\n (result as any).template = data.template;\n (result as any).template_variables = data.template_variables || [];\n }\n\n return result as SendMsg;\n },\n sanitize: (formData: FormData): void => {\n if (formData.text && typeof formData.text === 'string') {\n formData.text = formData.text.trim();\n }\n },\n validate: (formData: FormData): ValidationResult => {\n const errors: { [key: string]: string } = {};\n\n // Check total attachment count (static + runtime should not exceed 10)\n const staticAttachments = formData.attachments || [];\n const runtimeAttachments = (formData.runtime_attachments || []).filter(\n (item: any) => item && item.expression && item.expression.trim() !== ''\n );\n\n const totalAttachments =\n staticAttachments.length + runtimeAttachments.length;\n if (totalAttachments > 10) {\n if (runtimeAttachments.length > 0) {\n errors.runtime_attachments =\n 'Each message can only have up to 10 attachments';\n }\n if (staticAttachments.length > 0) {\n errors.text = 'Each message can only have up to 10 total attachments';\n }\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors\n };\n },\n localizable: ['text', 'quick_replies', 'attachments'],\n toLocalizationFormData: (\n action: SendMsg,\n localization: Record<string, any>\n ) => {\n // Convert localized values to form data format\n // Localized values are stored as arrays even for single values\n const formData: FormData = {\n uuid: action.uuid\n };\n\n // Handle text (single value, but stored as array in localization)\n if (localization.text && Array.isArray(localization.text)) {\n formData.text = localization.text[0] || '';\n } else {\n // Fall back to empty string if no localization\n formData.text = '';\n }\n\n // Handle attachments (already an array)\n if (localization.attachments && Array.isArray(localization.attachments)) {\n formData.attachments = localization.attachments;\n }\n\n // Handle quick_replies (already an array)\n if (\n localization.quick_replies &&\n Array.isArray(localization.quick_replies)\n ) {\n formData.quick_replies = localization.quick_replies.map((reply) => ({\n name: reply,\n value: reply\n }));\n }\n\n // Extract runtime attachments from localized attachments\n const runtimeAttachments: {\n type: { name: string; value: string };\n expression: string;\n }[] = [];\n const staticAttachments: string[] = [];\n\n if (formData.attachments && Array.isArray(formData.attachments)) {\n formData.attachments.forEach((attachment) => {\n if (typeof attachment === 'string' && attachment.includes(':')) {\n const colonIndex = attachment.indexOf(':');\n const contentType = attachment.substring(0, colonIndex);\n const value = attachment.substring(colonIndex + 1);\n\n if (!contentType.includes('/')) {\n runtimeAttachments.push({\n type: { name: titleCase(contentType), value: contentType },\n expression: value\n });\n } else {\n staticAttachments.push(attachment);\n }\n }\n });\n }\n\n formData.attachments = staticAttachments;\n formData.runtime_attachments = runtimeAttachments;\n\n return formData;\n },\n fromLocalizationFormData: (formData: FormData, action: SendMsg) => {\n // Convert form data to localization format\n // All values must be stored as arrays\n const localization: Record<string, any> = {};\n\n // Handle text (store as single-element array)\n // Only save if not empty and different from base action\n if (formData.text && formData.text.trim() !== '') {\n if (formData.text !== action.text) {\n localization.text = [formData.text];\n }\n }\n\n // Handle quick_replies (store as array)\n const quickReplies = (formData.quick_replies || [])\n .map((reply: any) =>\n typeof reply === 'string' ? reply : reply.value || reply.name || reply\n )\n .filter((reply: string) => reply && reply.trim() !== '');\n\n // Only save if there are quick replies and different from base action\n if (quickReplies.length > 0) {\n if (\n JSON.stringify(quickReplies) !==\n JSON.stringify(action.quick_replies || [])\n ) {\n localization.quick_replies = quickReplies;\n }\n }\n\n // Handle attachments (combine static and runtime attachments)\n const staticAttachments = (formData.attachments || []).filter(\n (att: string) => att && att.trim() !== ''\n );\n const runtimeAttachments = (formData.runtime_attachments || [])\n .filter(\n (item: {\n type: [{ name: string; value: string }];\n expression: string;\n }) =>\n item && item.type && item.expression && item.expression.trim() !== ''\n )\n .map(\n (item: {\n type: [{ name: string; value: string }];\n expression: string;\n }) => `${item.type[0].value}:${item.expression}`\n );\n\n const allAttachments = [...staticAttachments, ...runtimeAttachments];\n\n // Only save if there are attachments and different from base action\n if (allAttachments.length > 0) {\n if (\n JSON.stringify(allAttachments) !==\n JSON.stringify(action.attachments || [])\n ) {\n localization.attachments = allAttachments;\n }\n }\n\n return localization;\n }\n};\n"]}
@@ -1,11 +1,12 @@
1
1
  import { html } from 'lit-html';
2
2
  import { ACTION_GROUPS, FlowTypes } from '../types';
3
+ import { renderClamped } from '../utils';
3
4
  export const set_contact_channel = {
4
5
  name: 'Update Channel',
5
6
  group: ACTION_GROUPS.contacts,
6
7
  flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],
7
8
  render: (_node, action) => {
8
- return html `<div>Set to <strong>${action.channel.name}</strong></div>`;
9
+ return renderClamped(html `Set to <strong>${action.channel.name}</strong>`, `Set to ${action.channel.name}`);
9
10
  },
10
11
  form: {
11
12
  channel: {
@@ -1 +1 @@
1
- {"version":3,"file":"set_contact_channel.js","sourceRoot":"","sources":["../../../../src/flow/actions/set_contact_channel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAgB,aAAa,EAAY,SAAS,EAAE,MAAM,UAAU,CAAC;AAG5E,MAAM,CAAC,MAAM,mBAAmB,GAAiB;IAC/C,IAAI,EAAE,gBAAgB;IACtB,KAAK,EAAE,aAAa,CAAC,QAAQ;IAC7B,SAAS,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,UAAU,CAAC;IACrE,MAAM,EAAE,CAAC,KAAW,EAAE,MAAyB,EAAE,EAAE;QACjD,OAAO,IAAI,CAAA,uBAAuB,MAAM,CAAC,OAAO,CAAC,IAAI,iBAAiB,CAAC;IACzE,CAAC;IACD,IAAI,EAAE;QACJ,OAAO,EAAE;YACP,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,SAAS;YAChB,QAAQ,EAAE,IAAI;YACd,UAAU,EAAE,IAAI;YAChB,SAAS,EAAE,KAAK;YAChB,QAAQ,EAAE,uBAAuB;YACjC,QAAQ,EAAE,MAAM;YAChB,OAAO,EAAE,MAAM;YACf,WAAW,EAAE,gBAAgB;YAC7B,QAAQ,EAAE,2CAA2C;SACtD;KACF;IACD,UAAU,EAAE,CAAC,MAAyB,EAAE,EAAE;QACxC,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI;SAClD,CAAC;IACJ,CAAC;IACD,YAAY,EAAE,CAAC,QAAkB,EAAqB,EAAE;;QACtD,MAAM,OAAO,GAAG,MAAA,QAAQ,CAAC,OAAO,0CAAG,CAAC,CAAC,CAAC;QACtC,OAAO;YACL,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,IAAI,EAAE,qBAAqB;YAC3B,OAAO,EAAE;gBACP,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,KAAK;gBACnC,IAAI,EAAE,OAAO,CAAC,IAAI;aACnB;SACF,CAAC;IACJ,CAAC;CACF,CAAC","sourcesContent":["import { html } from 'lit-html';\nimport { ActionConfig, ACTION_GROUPS, FormData, FlowTypes } from '../types';\nimport { Node, SetContactChannel } from '../../store/flow-definition';\n\nexport const set_contact_channel: ActionConfig = {\n name: 'Update Channel',\n group: ACTION_GROUPS.contacts,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n render: (_node: Node, action: SetContactChannel) => {\n return html`<div>Set to <strong>${action.channel.name}</strong></div>`;\n },\n form: {\n channel: {\n type: 'select',\n label: 'Channel',\n required: true,\n searchable: true,\n clearable: false,\n endpoint: '/api/v2/channels.json',\n valueKey: 'uuid',\n nameKey: 'name',\n placeholder: 'Select channel',\n helpText: 'Select the channel to set for the contact'\n }\n },\n toFormData: (action: SetContactChannel) => {\n return {\n uuid: action.uuid,\n channel: action.channel ? [action.channel] : null\n };\n },\n fromFormData: (formData: FormData): SetContactChannel => {\n const channel = formData.channel?.[0];\n return {\n uuid: formData.uuid,\n type: 'set_contact_channel',\n channel: {\n uuid: channel.uuid || channel.value,\n name: channel.name\n }\n };\n }\n};\n"]}
1
+ {"version":3,"file":"set_contact_channel.js","sourceRoot":"","sources":["../../../../src/flow/actions/set_contact_channel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAgB,aAAa,EAAY,SAAS,EAAE,MAAM,UAAU,CAAC;AAE5E,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,CAAC,MAAM,mBAAmB,GAAiB;IAC/C,IAAI,EAAE,gBAAgB;IACtB,KAAK,EAAE,aAAa,CAAC,QAAQ;IAC7B,SAAS,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,UAAU,CAAC;IACrE,MAAM,EAAE,CAAC,KAAW,EAAE,MAAyB,EAAE,EAAE;QACjD,OAAO,aAAa,CAClB,IAAI,CAAA,kBAAkB,MAAM,CAAC,OAAO,CAAC,IAAI,WAAW,EACpD,UAAU,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAChC,CAAC;IACJ,CAAC;IACD,IAAI,EAAE;QACJ,OAAO,EAAE;YACP,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,SAAS;YAChB,QAAQ,EAAE,IAAI;YACd,UAAU,EAAE,IAAI;YAChB,SAAS,EAAE,KAAK;YAChB,QAAQ,EAAE,uBAAuB;YACjC,QAAQ,EAAE,MAAM;YAChB,OAAO,EAAE,MAAM;YACf,WAAW,EAAE,gBAAgB;YAC7B,QAAQ,EAAE,2CAA2C;SACtD;KACF;IACD,UAAU,EAAE,CAAC,MAAyB,EAAE,EAAE;QACxC,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI;SAClD,CAAC;IACJ,CAAC;IACD,YAAY,EAAE,CAAC,QAAkB,EAAqB,EAAE;;QACtD,MAAM,OAAO,GAAG,MAAA,QAAQ,CAAC,OAAO,0CAAG,CAAC,CAAC,CAAC;QACtC,OAAO;YACL,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,IAAI,EAAE,qBAAqB;YAC3B,OAAO,EAAE;gBACP,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,KAAK;gBACnC,IAAI,EAAE,OAAO,CAAC,IAAI;aACnB;SACF,CAAC;IACJ,CAAC;CACF,CAAC","sourcesContent":["import { html } from 'lit-html';\nimport { ActionConfig, ACTION_GROUPS, FormData, FlowTypes } from '../types';\nimport { Node, SetContactChannel } from '../../store/flow-definition';\nimport { renderClamped } from '../utils';\n\nexport const set_contact_channel: ActionConfig = {\n name: 'Update Channel',\n group: ACTION_GROUPS.contacts,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n render: (_node: Node, action: SetContactChannel) => {\n return renderClamped(\n html`Set to <strong>${action.channel.name}</strong>`,\n `Set to ${action.channel.name}`\n );\n },\n form: {\n channel: {\n type: 'select',\n label: 'Channel',\n required: true,\n searchable: true,\n clearable: false,\n endpoint: '/api/v2/channels.json',\n valueKey: 'uuid',\n nameKey: 'name',\n placeholder: 'Select channel',\n helpText: 'Select the channel to set for the contact'\n }\n },\n toFormData: (action: SetContactChannel) => {\n return {\n uuid: action.uuid,\n channel: action.channel ? [action.channel] : null\n };\n },\n fromFormData: (formData: FormData): SetContactChannel => {\n const channel = formData.channel?.[0];\n return {\n uuid: formData.uuid,\n type: 'set_contact_channel',\n channel: {\n uuid: channel.uuid || channel.value,\n name: channel.name\n }\n };\n }\n};\n"]}
@@ -1,18 +1,17 @@
1
1
  import { html } from 'lit-html';
2
2
  import { ACTION_GROUPS, FlowTypes } from '../types';
3
+ import { renderClamped } from '../utils';
3
4
  export const set_contact_field = {
4
5
  name: 'Update Field',
5
6
  group: ACTION_GROUPS.contacts,
6
7
  flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],
7
8
  render: (_node, action) => {
8
9
  if (action.value) {
9
- return html `<div>
10
- Set <strong>${action.field.name}</strong> to
11
- <strong>${action.value}</strong>
12
- </div>`;
10
+ return renderClamped(html `Set <strong>${action.field.name}</strong> to
11
+ <strong>${action.value}</strong>`, `Set ${action.field.name} to ${action.value}`);
13
12
  }
14
13
  else {
15
- return html `<div>Clear <strong>${action.field.name}</strong></div>`;
14
+ return renderClamped(html `Clear <strong>${action.field.name}</strong>`, `Clear ${action.field.name}`);
16
15
  }
17
16
  },
18
17
  form: {
@@ -1 +1 @@
1
- {"version":3,"file":"set_contact_field.js","sourceRoot":"","sources":["../../../../src/flow/actions/set_contact_field.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAgB,aAAa,EAAY,SAAS,EAAE,MAAM,UAAU,CAAC;AAG5E,MAAM,CAAC,MAAM,iBAAiB,GAAiB;IAC7C,IAAI,EAAE,cAAc;IACpB,KAAK,EAAE,aAAa,CAAC,QAAQ;IAC7B,SAAS,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,UAAU,CAAC;IACrE,MAAM,EAAE,CAAC,KAAW,EAAE,MAAuB,EAAE,EAAE;QAC/C,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,OAAO,IAAI,CAAA;sBACK,MAAM,CAAC,KAAK,CAAC,IAAI;kBACrB,MAAM,CAAC,KAAK;aACjB,CAAC;QACV,CAAC;aAAM,CAAC;YACN,OAAO,IAAI,CAAA,sBAAsB,MAAM,CAAC,KAAK,CAAC,IAAI,iBAAiB,CAAC;QACtE,CAAC;IACH,CAAC;IACD,IAAI,EAAE;QACJ,KAAK,EAAE;YACL,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,OAAO;YACd,QAAQ,EAAE,IAAI;YACd,UAAU,EAAE,IAAI;YAChB,SAAS,EAAE,KAAK;YAChB,WAAW,EAAE,8BAA8B;YAC3C,OAAO,EAAE,MAAM;YACf,QAAQ,EAAE,KAAK;YACf,QAAQ,EAAE,qBAAqB;YAC/B,QAAQ,EAAE,oCAAoC;YAC9C,WAAW,EAAE,IAAI;YACjB,qBAAqB,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC;gBACzC,GAAG,EAAE,KAAK;gBACV,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE,MAAM;aACb,CAAC;SACH;QACD,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,OAAO;YACd,WAAW,EAAE,sBAAsB;YACnC,SAAS,EAAE,IAAI;YACf,QAAQ,EACN,iFAAiF;SACpF;KACF;IACD,UAAU,EAAE,CAAC,MAAuB,EAAE,EAAE;QACtC,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI;YAC3C,KAAK,EAAE,MAAM,CAAC,KAAK;SACpB,CAAC;IACJ,CAAC;IACD,YAAY,EAAE,CAAC,QAAkB,EAAmB,EAAE;QACpD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAChC,OAAO;YACL,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,IAAI,EAAE,mBAAmB;YACzB,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE;YAC3C,KAAK,EAAE,QAAQ,CAAC,KAAK;SACtB,CAAC;IACJ,CAAC;IACD,QAAQ,EAAE,CAAC,QAAkB,EAAQ,EAAE;QACrC,IAAI,QAAQ,CAAC,KAAK,IAAI,OAAO,QAAQ,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACzD,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACzC,CAAC;IACH,CAAC;CACF,CAAC","sourcesContent":["import { html } from 'lit-html';\nimport { ActionConfig, ACTION_GROUPS, FormData, FlowTypes } from '../types';\nimport { Node, SetContactField } from '../../store/flow-definition';\n\nexport const set_contact_field: ActionConfig = {\n name: 'Update Field',\n group: ACTION_GROUPS.contacts,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n render: (_node: Node, action: SetContactField) => {\n if (action.value) {\n return html`<div>\n Set <strong>${action.field.name}</strong> to\n <strong>${action.value}</strong>\n </div>`;\n } else {\n return html`<div>Clear <strong>${action.field.name}</strong></div>`;\n }\n },\n form: {\n field: {\n type: 'select',\n label: 'Field',\n required: true,\n searchable: true,\n clearable: false,\n placeholder: 'Search for contact fields...',\n nameKey: 'name',\n valueKey: 'key',\n endpoint: '/api/v2/fields.json',\n helpText: 'Select the contact field to update',\n allowCreate: true,\n createArbitraryOption: (input: string) => ({\n key: input,\n name: input,\n type: 'text'\n })\n },\n value: {\n type: 'text',\n label: 'Value',\n placeholder: 'Enter field value...',\n evaluated: true,\n helpText:\n 'The new value for the contact field. You can use expressions like @contact.name'\n }\n },\n toFormData: (action: SetContactField) => {\n return {\n uuid: action.uuid,\n field: action.field ? [action.field] : null,\n value: action.value\n };\n },\n fromFormData: (formData: FormData): SetContactField => {\n const field = formData.field[0];\n return {\n uuid: formData.uuid,\n type: 'set_contact_field',\n field: { name: field.name, key: field.key },\n value: formData.value\n };\n },\n sanitize: (formData: FormData): void => {\n if (formData.value && typeof formData.value === 'string') {\n formData.value = formData.value.trim();\n }\n }\n};\n"]}
1
+ {"version":3,"file":"set_contact_field.js","sourceRoot":"","sources":["../../../../src/flow/actions/set_contact_field.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAgB,aAAa,EAAY,SAAS,EAAE,MAAM,UAAU,CAAC;AAE5E,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,CAAC,MAAM,iBAAiB,GAAiB;IAC7C,IAAI,EAAE,cAAc;IACpB,KAAK,EAAE,aAAa,CAAC,QAAQ;IAC7B,SAAS,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,UAAU,CAAC;IACrE,MAAM,EAAE,CAAC,KAAW,EAAE,MAAuB,EAAE,EAAE;QAC/C,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,OAAO,aAAa,CAClB,IAAI,CAAA,eAAe,MAAM,CAAC,KAAK,CAAC,IAAI;oBACxB,MAAM,CAAC,KAAK,WAAW,EACnC,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,OAAO,MAAM,CAAC,KAAK,EAAE,CAC9C,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,OAAO,aAAa,CAClB,IAAI,CAAA,iBAAiB,MAAM,CAAC,KAAK,CAAC,IAAI,WAAW,EACjD,SAAS,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAC7B,CAAC;QACJ,CAAC;IACH,CAAC;IACD,IAAI,EAAE;QACJ,KAAK,EAAE;YACL,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,OAAO;YACd,QAAQ,EAAE,IAAI;YACd,UAAU,EAAE,IAAI;YAChB,SAAS,EAAE,KAAK;YAChB,WAAW,EAAE,8BAA8B;YAC3C,OAAO,EAAE,MAAM;YACf,QAAQ,EAAE,KAAK;YACf,QAAQ,EAAE,qBAAqB;YAC/B,QAAQ,EAAE,oCAAoC;YAC9C,WAAW,EAAE,IAAI;YACjB,qBAAqB,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC;gBACzC,GAAG,EAAE,KAAK;gBACV,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE,MAAM;aACb,CAAC;SACH;QACD,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,OAAO;YACd,WAAW,EAAE,sBAAsB;YACnC,SAAS,EAAE,IAAI;YACf,QAAQ,EACN,iFAAiF;SACpF;KACF;IACD,UAAU,EAAE,CAAC,MAAuB,EAAE,EAAE;QACtC,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI;YAC3C,KAAK,EAAE,MAAM,CAAC,KAAK;SACpB,CAAC;IACJ,CAAC;IACD,YAAY,EAAE,CAAC,QAAkB,EAAmB,EAAE;QACpD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAChC,OAAO;YACL,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,IAAI,EAAE,mBAAmB;YACzB,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE;YAC3C,KAAK,EAAE,QAAQ,CAAC,KAAK;SACtB,CAAC;IACJ,CAAC;IACD,QAAQ,EAAE,CAAC,QAAkB,EAAQ,EAAE;QACrC,IAAI,QAAQ,CAAC,KAAK,IAAI,OAAO,QAAQ,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACzD,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACzC,CAAC;IACH,CAAC;CACF,CAAC","sourcesContent":["import { html } from 'lit-html';\nimport { ActionConfig, ACTION_GROUPS, FormData, FlowTypes } from '../types';\nimport { Node, SetContactField } from '../../store/flow-definition';\nimport { renderClamped } from '../utils';\n\nexport const set_contact_field: ActionConfig = {\n name: 'Update Field',\n group: ACTION_GROUPS.contacts,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n render: (_node: Node, action: SetContactField) => {\n if (action.value) {\n return renderClamped(\n html`Set <strong>${action.field.name}</strong> to\n <strong>${action.value}</strong>`,\n `Set ${action.field.name} to ${action.value}`\n );\n } else {\n return renderClamped(\n html`Clear <strong>${action.field.name}</strong>`,\n `Clear ${action.field.name}`\n );\n }\n },\n form: {\n field: {\n type: 'select',\n label: 'Field',\n required: true,\n searchable: true,\n clearable: false,\n placeholder: 'Search for contact fields...',\n nameKey: 'name',\n valueKey: 'key',\n endpoint: '/api/v2/fields.json',\n helpText: 'Select the contact field to update',\n allowCreate: true,\n createArbitraryOption: (input: string) => ({\n key: input,\n name: input,\n type: 'text'\n })\n },\n value: {\n type: 'text',\n label: 'Value',\n placeholder: 'Enter field value...',\n evaluated: true,\n helpText:\n 'The new value for the contact field. You can use expressions like @contact.name'\n }\n },\n toFormData: (action: SetContactField) => {\n return {\n uuid: action.uuid,\n field: action.field ? [action.field] : null,\n value: action.value\n };\n },\n fromFormData: (formData: FormData): SetContactField => {\n const field = formData.field[0];\n return {\n uuid: formData.uuid,\n type: 'set_contact_field',\n field: { name: field.name, key: field.key },\n value: formData.value\n };\n },\n sanitize: (formData: FormData): void => {\n if (formData.value && typeof formData.value === 'string') {\n formData.value = formData.value.trim();\n }\n }\n};\n"]}
@@ -1,6 +1,7 @@
1
1
  import { html } from 'lit-html';
2
2
  import { ACTION_GROUPS, FlowTypes } from '../types';
3
3
  import { getStore } from '../../store/Store';
4
+ import { renderClamped } from '../utils';
4
5
  export const set_contact_language = {
5
6
  name: 'Update Language',
6
7
  group: ACTION_GROUPS.contacts,
@@ -9,9 +10,8 @@ export const set_contact_language = {
9
10
  const languageNames = new Intl.DisplayNames(['en'], {
10
11
  type: 'language'
11
12
  });
12
- return html `<div>
13
- Set to <strong>${languageNames.of(action.language)}</strong>
14
- </div>`;
13
+ const name = languageNames.of(action.language) || action.language;
14
+ return renderClamped(html `Set to <strong>${name}</strong>`, `Set to ${name}`);
15
15
  },
16
16
  form: {
17
17
  language: {
@@ -1 +1 @@
1
- {"version":3,"file":"set_contact_language.js","sourceRoot":"","sources":["../../../../src/flow/actions/set_contact_language.ts"],"names":[],"mappings":"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","sourcesContent":["import { html } from 'lit-html';\nimport {\n ActionConfig,\n ACTION_GROUPS,\n FormData,\n ValidationResult,\n FlowTypes\n} from '../types';\nimport { Node, SetContactLanguage } from '../../store/flow-definition';\nimport { getStore } from '../../store/Store';\n\nexport const set_contact_language: ActionConfig = {\n name: 'Update Language',\n group: ACTION_GROUPS.contacts,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n render: (_node: Node, action: SetContactLanguage) => {\n const languageNames = new Intl.DisplayNames(['en'], {\n type: 'language'\n });\n return html`<div>\n Set to <strong>${languageNames.of(action.language)}</strong>\n </div>`;\n },\n form: {\n language: {\n type: 'select',\n label: 'Language',\n required: true,\n searchable: true,\n clearable: false,\n valueKey: 'value',\n nameKey: 'name',\n helpText: 'Select the language to set for the contact',\n getDynamicOptions: () => {\n const store = getStore();\n const workspace = store?.getState().workspace;\n if (workspace?.languages && Array.isArray(workspace.languages)) {\n const languageNames = new Intl.DisplayNames(['en'], {\n type: 'language'\n });\n return workspace.languages.map((languageCode: string) => ({\n value: languageCode,\n name: languageNames.of(languageCode) || languageCode\n }));\n }\n return [];\n }\n }\n },\n toFormData: (action: SetContactLanguage) => {\n // Convert the language code back to the option object format expected by the form\n if (action.language) {\n const languageNames = new Intl.DisplayNames(['en'], {\n type: 'language'\n });\n return {\n language: [\n {\n value: action.language,\n name: languageNames.of(action.language) || action.language\n }\n ],\n uuid: action.uuid\n };\n }\n return {\n language: null,\n uuid: action.uuid\n };\n },\n fromFormData: (formData: FormData): SetContactLanguage => {\n return {\n uuid: formData.uuid,\n type: 'set_contact_language',\n language: formData.language[0].value\n };\n },\n\n validate: (formData: FormData): ValidationResult => {\n const errors: { [key: string]: string } = {};\n\n if (!formData.language) {\n errors.language = 'Language is required';\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors\n };\n }\n};\n"]}
1
+ {"version":3,"file":"set_contact_language.js","sourceRoot":"","sources":["../../../../src/flow/actions/set_contact_language.ts"],"names":[],"mappings":"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","sourcesContent":["import { html } from 'lit-html';\nimport {\n ActionConfig,\n ACTION_GROUPS,\n FormData,\n ValidationResult,\n FlowTypes\n} from '../types';\nimport { Node, SetContactLanguage } from '../../store/flow-definition';\nimport { getStore } from '../../store/Store';\nimport { renderClamped } from '../utils';\n\nexport const set_contact_language: ActionConfig = {\n name: 'Update Language',\n group: ACTION_GROUPS.contacts,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n render: (_node: Node, action: SetContactLanguage) => {\n const languageNames = new Intl.DisplayNames(['en'], {\n type: 'language'\n });\n const name = languageNames.of(action.language) || action.language;\n return renderClamped(\n html`Set to <strong>${name}</strong>`,\n `Set to ${name}`\n );\n },\n form: {\n language: {\n type: 'select',\n label: 'Language',\n required: true,\n searchable: true,\n clearable: false,\n valueKey: 'value',\n nameKey: 'name',\n helpText: 'Select the language to set for the contact',\n getDynamicOptions: () => {\n const store = getStore();\n const workspace = store?.getState().workspace;\n if (workspace?.languages && Array.isArray(workspace.languages)) {\n const languageNames = new Intl.DisplayNames(['en'], {\n type: 'language'\n });\n return workspace.languages.map((languageCode: string) => ({\n value: languageCode,\n name: languageNames.of(languageCode) || languageCode\n }));\n }\n return [];\n }\n }\n },\n toFormData: (action: SetContactLanguage) => {\n // Convert the language code back to the option object format expected by the form\n if (action.language) {\n const languageNames = new Intl.DisplayNames(['en'], {\n type: 'language'\n });\n return {\n language: [\n {\n value: action.language,\n name: languageNames.of(action.language) || action.language\n }\n ],\n uuid: action.uuid\n };\n }\n return {\n language: null,\n uuid: action.uuid\n };\n },\n fromFormData: (formData: FormData): SetContactLanguage => {\n return {\n uuid: formData.uuid,\n type: 'set_contact_language',\n language: formData.language[0].value\n };\n },\n\n validate: (formData: FormData): ValidationResult => {\n const errors: { [key: string]: string } = {};\n\n if (!formData.language) {\n errors.language = 'Language is required';\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors\n };\n }\n};\n"]}