@nyaruka/temba-components 0.131.2 → 0.131.3

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 (223) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/demo/components/floating-tabs/example.html +400 -0
  3. package/demo/components/flow/index.html +1 -1
  4. package/demo/data/flows/sample-flow.json +41 -2
  5. package/demo/data/flows/voicemail.json +613 -0
  6. package/demo/index.html +6 -0
  7. package/dist/locales/es.js +5 -5
  8. package/dist/locales/es.js.map +1 -1
  9. package/dist/locales/fr.js +5 -5
  10. package/dist/locales/fr.js.map +1 -1
  11. package/dist/locales/locale-codes.js +11 -2
  12. package/dist/locales/locale-codes.js.map +1 -1
  13. package/dist/locales/pt.js +5 -5
  14. package/dist/locales/pt.js.map +1 -1
  15. package/dist/temba-components.js +1109 -535
  16. package/dist/temba-components.js.map +1 -1
  17. package/out-tsc/src/display/FloatingTab.js +167 -0
  18. package/out-tsc/src/display/FloatingTab.js.map +1 -0
  19. package/out-tsc/src/display/ProgressBar.js +22 -2
  20. package/out-tsc/src/display/ProgressBar.js.map +1 -1
  21. package/out-tsc/src/events.js.map +1 -1
  22. package/out-tsc/src/flow/CanvasNode.js +165 -31
  23. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  24. package/out-tsc/src/flow/Editor.js +857 -3
  25. package/out-tsc/src/flow/Editor.js.map +1 -1
  26. package/out-tsc/src/flow/NodeEditor.js +239 -19
  27. package/out-tsc/src/flow/NodeEditor.js.map +1 -1
  28. package/out-tsc/src/flow/NodeTypeSelector.js +44 -3
  29. package/out-tsc/src/flow/NodeTypeSelector.js.map +1 -1
  30. package/out-tsc/src/flow/StickyNote.js +12 -3
  31. package/out-tsc/src/flow/StickyNote.js.map +1 -1
  32. package/out-tsc/src/flow/actions/add_contact_groups.js +2 -1
  33. package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
  34. package/out-tsc/src/flow/actions/add_contact_urn.js +2 -1
  35. package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
  36. package/out-tsc/src/flow/actions/add_input_labels.js +2 -1
  37. package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
  38. package/out-tsc/src/flow/actions/play_audio.js +2 -1
  39. package/out-tsc/src/flow/actions/play_audio.js.map +1 -1
  40. package/out-tsc/src/flow/actions/remove_contact_groups.js +2 -1
  41. package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -1
  42. package/out-tsc/src/flow/actions/request_optin.js +1 -0
  43. package/out-tsc/src/flow/actions/request_optin.js.map +1 -1
  44. package/out-tsc/src/flow/actions/say_msg.js +2 -1
  45. package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
  46. package/out-tsc/src/flow/actions/send_broadcast.js +2 -1
  47. package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
  48. package/out-tsc/src/flow/actions/send_email.js +2 -1
  49. package/out-tsc/src/flow/actions/send_email.js.map +1 -1
  50. package/out-tsc/src/flow/actions/send_msg.js +93 -3
  51. package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
  52. package/out-tsc/src/flow/actions/set_contact_channel.js +2 -1
  53. package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
  54. package/out-tsc/src/flow/actions/set_contact_field.js +2 -1
  55. package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
  56. package/out-tsc/src/flow/actions/set_contact_language.js +2 -1
  57. package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
  58. package/out-tsc/src/flow/actions/set_contact_name.js +2 -1
  59. package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
  60. package/out-tsc/src/flow/actions/set_contact_status.js +2 -1
  61. package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
  62. package/out-tsc/src/flow/actions/set_run_result.js +2 -1
  63. package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
  64. package/out-tsc/src/flow/actions/start_session.js +2 -1
  65. package/out-tsc/src/flow/actions/start_session.js.map +1 -1
  66. package/out-tsc/src/flow/config.js +2 -10
  67. package/out-tsc/src/flow/config.js.map +1 -1
  68. package/out-tsc/src/flow/nodes/shared.js +54 -0
  69. package/out-tsc/src/flow/nodes/shared.js.map +1 -1
  70. package/out-tsc/src/flow/nodes/split_by_airtime.js +9 -3
  71. package/out-tsc/src/flow/nodes/split_by_airtime.js.map +1 -1
  72. package/out-tsc/src/flow/nodes/split_by_contact_field.js +8 -3
  73. package/out-tsc/src/flow/nodes/split_by_contact_field.js.map +1 -1
  74. package/out-tsc/src/flow/nodes/split_by_expression.js +8 -3
  75. package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -1
  76. package/out-tsc/src/flow/nodes/split_by_groups.js +8 -3
  77. package/out-tsc/src/flow/nodes/split_by_groups.js.map +1 -1
  78. package/out-tsc/src/flow/nodes/split_by_intent.js +3 -2
  79. package/out-tsc/src/flow/nodes/split_by_intent.js.map +1 -1
  80. package/out-tsc/src/flow/nodes/split_by_llm.js +9 -2
  81. package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -1
  82. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +9 -2
  83. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
  84. package/out-tsc/src/flow/nodes/split_by_random.js +8 -2
  85. package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
  86. package/out-tsc/src/flow/nodes/split_by_resthook.js +8 -3
  87. package/out-tsc/src/flow/nodes/split_by_resthook.js.map +1 -1
  88. package/out-tsc/src/flow/nodes/split_by_run_result.js +8 -3
  89. package/out-tsc/src/flow/nodes/split_by_run_result.js.map +1 -1
  90. package/out-tsc/src/flow/nodes/split_by_scheme.js +8 -3
  91. package/out-tsc/src/flow/nodes/split_by_scheme.js.map +1 -1
  92. package/out-tsc/src/flow/nodes/split_by_subflow.js +8 -2
  93. package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
  94. package/out-tsc/src/flow/nodes/split_by_ticket.js +8 -2
  95. package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -1
  96. package/out-tsc/src/flow/nodes/split_by_webhook.js +8 -2
  97. package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
  98. package/out-tsc/src/flow/nodes/wait_for_digits.js +3 -2
  99. package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
  100. package/out-tsc/src/flow/nodes/wait_for_menu.js +3 -2
  101. package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -1
  102. package/out-tsc/src/flow/nodes/wait_for_response.js +8 -3
  103. package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
  104. package/out-tsc/src/flow/types.js +15 -0
  105. package/out-tsc/src/flow/types.js.map +1 -1
  106. package/out-tsc/src/layout/FloatingWindow.js +346 -0
  107. package/out-tsc/src/layout/FloatingWindow.js.map +1 -0
  108. package/out-tsc/src/live/ContactChat.js +3 -19
  109. package/out-tsc/src/live/ContactChat.js.map +1 -1
  110. package/out-tsc/src/locales/es.js +5 -5
  111. package/out-tsc/src/locales/es.js.map +1 -1
  112. package/out-tsc/src/locales/fr.js +5 -5
  113. package/out-tsc/src/locales/fr.js.map +1 -1
  114. package/out-tsc/src/locales/locale-codes.js +11 -2
  115. package/out-tsc/src/locales/locale-codes.js.map +1 -1
  116. package/out-tsc/src/locales/pt.js +5 -5
  117. package/out-tsc/src/locales/pt.js.map +1 -1
  118. package/out-tsc/src/store/AppState.js +67 -0
  119. package/out-tsc/src/store/AppState.js.map +1 -1
  120. package/out-tsc/temba-modules.js +4 -0
  121. package/out-tsc/temba-modules.js.map +1 -1
  122. package/out-tsc/test/temba-floating-tab.test.js +91 -0
  123. package/out-tsc/test/temba-floating-tab.test.js.map +1 -0
  124. package/out-tsc/test/temba-floating-window.test.js +301 -0
  125. package/out-tsc/test/temba-floating-window.test.js.map +1 -0
  126. package/out-tsc/test/temba-flow-editor-node.test.js +117 -0
  127. package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
  128. package/out-tsc/test/temba-localization.test.js +471 -0
  129. package/out-tsc/test/temba-localization.test.js.map +1 -0
  130. package/out-tsc/test/temba-node-type-selector.test.js +150 -0
  131. package/out-tsc/test/temba-node-type-selector.test.js.map +1 -1
  132. package/out-tsc/test/utils.test.js +18 -0
  133. package/out-tsc/test/utils.test.js.map +1 -1
  134. package/package.json +1 -1
  135. package/screenshots/truth/floating-tab/default.png +0 -0
  136. package/screenshots/truth/floating-tab/gray.png +0 -0
  137. package/screenshots/truth/floating-tab/green.png +0 -0
  138. package/screenshots/truth/floating-tab/hidden.png +0 -0
  139. package/screenshots/truth/floating-tab/hover.png +0 -0
  140. package/screenshots/truth/floating-tab/purple.png +0 -0
  141. package/screenshots/truth/floating-window/chromeless.png +0 -0
  142. package/screenshots/truth/floating-window/custom-size.png +0 -0
  143. package/screenshots/truth/floating-window/default.png +0 -0
  144. package/screenshots/truth/floating-window/with-header.png +0 -0
  145. package/screenshots/truth/node-type-selector/action-mode.png +0 -0
  146. package/screenshots/truth/node-type-selector/split-mode.png +0 -0
  147. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  148. package/src/display/FloatingTab.ts +174 -0
  149. package/src/display/ProgressBar.ts +22 -2
  150. package/src/events.ts +2 -4
  151. package/src/flow/CanvasNode.ts +190 -32
  152. package/src/flow/Editor.ts +1040 -3
  153. package/src/flow/NodeEditor.ts +317 -19
  154. package/src/flow/NodeTypeSelector.ts +47 -3
  155. package/src/flow/StickyNote.ts +12 -3
  156. package/src/flow/actions/add_contact_groups.ts +2 -1
  157. package/src/flow/actions/add_contact_urn.ts +3 -1
  158. package/src/flow/actions/add_input_labels.ts +2 -1
  159. package/src/flow/actions/play_audio.ts +2 -1
  160. package/src/flow/actions/remove_contact_groups.ts +3 -1
  161. package/src/flow/actions/request_optin.ts +1 -0
  162. package/src/flow/actions/say_msg.ts +2 -1
  163. package/src/flow/actions/send_broadcast.ts +2 -1
  164. package/src/flow/actions/send_email.ts +3 -1
  165. package/src/flow/actions/send_msg.ts +134 -3
  166. package/src/flow/actions/set_contact_channel.ts +2 -1
  167. package/src/flow/actions/set_contact_field.ts +2 -1
  168. package/src/flow/actions/set_contact_language.ts +3 -1
  169. package/src/flow/actions/set_contact_name.ts +2 -1
  170. package/src/flow/actions/set_contact_status.ts +2 -1
  171. package/src/flow/actions/set_run_result.ts +2 -1
  172. package/src/flow/actions/start_session.ts +3 -1
  173. package/src/flow/config.ts +2 -12
  174. package/src/flow/nodes/shared.ts +70 -1
  175. package/src/flow/nodes/split_by_airtime.ts +20 -3
  176. package/src/flow/nodes/split_by_contact_field.ts +13 -3
  177. package/src/flow/nodes/split_by_expression.ts +13 -3
  178. package/src/flow/nodes/split_by_groups.ts +13 -3
  179. package/src/flow/nodes/split_by_intent.ts +3 -2
  180. package/src/flow/nodes/split_by_llm.ts +19 -2
  181. package/src/flow/nodes/split_by_llm_categorize.ts +19 -2
  182. package/src/flow/nodes/split_by_random.ts +12 -2
  183. package/src/flow/nodes/split_by_resthook.ts +13 -3
  184. package/src/flow/nodes/split_by_run_result.ts +13 -3
  185. package/src/flow/nodes/split_by_scheme.ts +13 -3
  186. package/src/flow/nodes/split_by_subflow.ts +12 -2
  187. package/src/flow/nodes/split_by_ticket.ts +12 -2
  188. package/src/flow/nodes/split_by_webhook.ts +12 -2
  189. package/src/flow/nodes/wait_for_digits.ts +3 -2
  190. package/src/flow/nodes/wait_for_menu.ts +3 -2
  191. package/src/flow/nodes/wait_for_response.ts +13 -3
  192. package/src/flow/types.ts +47 -0
  193. package/src/layout/FloatingWindow.ts +386 -0
  194. package/src/live/ContactChat.ts +4 -19
  195. package/src/locales/es.ts +18 -13
  196. package/src/locales/fr.ts +18 -13
  197. package/src/locales/locale-codes.ts +11 -2
  198. package/src/locales/pt.ts +18 -13
  199. package/src/store/AppState.ts +104 -0
  200. package/static/api/llms.json +18 -0
  201. package/temba-modules.ts +4 -0
  202. package/test/temba-floating-tab.test.ts +110 -0
  203. package/test/temba-floating-window.test.ts +477 -0
  204. package/test/temba-flow-editor-node.test.ts +144 -0
  205. package/test/temba-localization.test.ts +611 -0
  206. package/test/temba-node-type-selector.test.ts +203 -0
  207. package/test/utils.test.ts +20 -0
  208. package/test-assets/contacts/history.json +5 -6
  209. package/test-assets/select/llms.json +2 -2
  210. package/web-dev-server.config.mjs +47 -1
  211. package/web-test-runner.config.mjs +0 -1
  212. package/out-tsc/src/flow/nodes/wait_for_audio.js +0 -7
  213. package/out-tsc/src/flow/nodes/wait_for_audio.js.map +0 -1
  214. package/out-tsc/src/flow/nodes/wait_for_image.js +0 -7
  215. package/out-tsc/src/flow/nodes/wait_for_image.js.map +0 -1
  216. package/out-tsc/src/flow/nodes/wait_for_location.js +0 -7
  217. package/out-tsc/src/flow/nodes/wait_for_location.js.map +0 -1
  218. package/out-tsc/src/flow/nodes/wait_for_video.js +0 -7
  219. package/out-tsc/src/flow/nodes/wait_for_video.js.map +0 -1
  220. package/src/flow/nodes/wait_for_audio.ts +0 -7
  221. package/src/flow/nodes/wait_for_image.ts +0 -7
  222. package/src/flow/nodes/wait_for_location.ts +0 -7
  223. package/src/flow/nodes/wait_for_video.ts +0 -7
@@ -8,6 +8,8 @@ import { CustomEventType } from '../interfaces';
8
8
  import { generateUUID } from '../utils';
9
9
  import { FieldRenderer } from '../form/FieldRenderer';
10
10
  import { renderMarkdownInline } from '../markdown';
11
+ import { fromStore, zustand } from '../store/AppState';
12
+ import { getStore } from '../store/Store';
11
13
  export class NodeEditor extends RapidElement {
12
14
  constructor() {
13
15
  super(...arguments);
@@ -314,6 +316,54 @@ export class NodeEditor extends RapidElement {
314
316
  .optional-field-link a:hover {
315
317
  text-decoration: underline;
316
318
  }
319
+
320
+ .original-value {
321
+ background: #fff8dc;
322
+ padding: 10px;
323
+ border-radius: 4px;
324
+ font-size: 13px;
325
+ color: #666;
326
+ }
327
+
328
+ .original-value-content {
329
+ color: #333;
330
+ white-space: pre-wrap;
331
+ word-break: break-word;
332
+ }
333
+
334
+ .category-localization-table {
335
+ width: 100%;
336
+ display: flex;
337
+ flex-direction: column;
338
+ gap: 5px;
339
+ }
340
+
341
+ .category-localization-row {
342
+ display: grid;
343
+ grid-template-columns: 40% 60%;
344
+ }
345
+
346
+ .category-localization-row:last-child {
347
+ border-bottom: none;
348
+ }
349
+
350
+ .original-name {
351
+ padding: 10px 20px;
352
+ background: #fff8dc;
353
+ display: flex;
354
+ align-items: center;
355
+ border-radius: var(--curvature);
356
+ }
357
+
358
+ .localized-name {
359
+ padding: 10px;
360
+ display: flex;
361
+ align-items: center;
362
+ }
363
+
364
+ .localized-name temba-textinput {
365
+ width: 100%;
366
+ }
317
367
  `;
318
368
  }
319
369
  connectedCallback() {
@@ -345,11 +395,20 @@ export class NodeEditor extends RapidElement {
345
395
  this.isOpen = true;
346
396
  }
347
397
  initializeFormData() {
398
+ var _a, _b, _c, _d, _e;
348
399
  const nodeConfig = this.getNodeConfig();
349
400
  if ((!nodeConfig || nodeConfig.type === 'execute_actions') && this.action) {
350
401
  // Action editing mode - use action config
351
402
  const actionConfig = ACTION_CONFIG[this.action.type];
352
- if (actionConfig === null || actionConfig === void 0 ? void 0 : actionConfig.toFormData) {
403
+ // Check if we're in localization mode
404
+ if (this.isTranslating &&
405
+ (actionConfig === null || actionConfig === void 0 ? void 0 : actionConfig.localizable) &&
406
+ actionConfig.toLocalizationFormData) {
407
+ // Get localized values for this action
408
+ const localization = ((_c = (_b = (_a = this.flowDefinition) === null || _a === void 0 ? void 0 : _a.localization) === null || _b === void 0 ? void 0 : _b[this.languageCode]) === null || _c === void 0 ? void 0 : _c[this.action.uuid]) || {};
409
+ this.formData = actionConfig.toLocalizationFormData(this.action, localization);
410
+ }
411
+ else if (actionConfig === null || actionConfig === void 0 ? void 0 : actionConfig.toFormData) {
353
412
  this.formData = actionConfig.toFormData(this.action);
354
413
  }
355
414
  else {
@@ -363,7 +422,15 @@ export class NodeEditor extends RapidElement {
363
422
  else if (this.node) {
364
423
  // Node editing mode - use node config
365
424
  const nodeConfig = this.getNodeConfig();
366
- if (nodeConfig === null || nodeConfig === void 0 ? void 0 : nodeConfig.toFormData) {
425
+ // Check if we're in localization mode for a node with localizable categories
426
+ if (this.isTranslating &&
427
+ (nodeConfig === null || nodeConfig === void 0 ? void 0 : nodeConfig.localizable) === 'categories' &&
428
+ nodeConfig.toLocalizationFormData) {
429
+ // Get localized values for this node's categories
430
+ const localization = ((_e = (_d = this.flowDefinition) === null || _d === void 0 ? void 0 : _d.localization) === null || _e === void 0 ? void 0 : _e[this.languageCode]) || {};
431
+ this.formData = nodeConfig.toLocalizationFormData(this.node, localization);
432
+ }
433
+ else if (nodeConfig === null || nodeConfig === void 0 ? void 0 : nodeConfig.toFormData) {
367
434
  this.formData = nodeConfig.toFormData(this.node, this.nodeUI);
368
435
  }
369
436
  else {
@@ -488,16 +555,63 @@ export class NodeEditor extends RapidElement {
488
555
  }
489
556
  }
490
557
  handleSave() {
491
- // Validate the form
492
- const validation = this.validateForm();
493
- if (!validation.valid) {
494
- this.errors = validation.errors;
495
- // Expand any groups that contain validation errors
496
- this.expandGroupsWithErrors(validation.errors);
497
- return;
498
- }
499
- // Process form data to convert key-value arrays to Records before saving
558
+ var _a, _b, _c, _d;
559
+ // Process form data first
500
560
  const processedFormData = this.processFormDataForSave();
561
+ // Skip validation if we're in localization mode
562
+ // (localization only deals with translating text, not changing structure)
563
+ if (!this.isTranslating) {
564
+ // Validate the form
565
+ const validation = this.validateForm();
566
+ if (!validation.valid) {
567
+ this.errors = validation.errors;
568
+ // Expand any groups that contain validation errors
569
+ this.expandGroupsWithErrors(validation.errors);
570
+ return;
571
+ }
572
+ }
573
+ // Check if we're in localization mode
574
+ if (this.isTranslating) {
575
+ // Handle action localization
576
+ if (this.action) {
577
+ const actionConfig = ACTION_CONFIG[this.action.type];
578
+ if ((actionConfig === null || actionConfig === void 0 ? void 0 : actionConfig.localizable) &&
579
+ actionConfig.fromLocalizationFormData) {
580
+ // Save to localization structure
581
+ const localizationData = actionConfig.fromLocalizationFormData(processedFormData, this.action);
582
+ // Update the flow definition's localization
583
+ this.updateLocalization(this.languageCode, this.action.uuid, localizationData);
584
+ // Close the dialog
585
+ this.fireCustomEvent(CustomEventType.NodeEditCancelled, {});
586
+ return;
587
+ }
588
+ }
589
+ // Handle node localization (for router categories)
590
+ if (this.node) {
591
+ const nodeConfig = this.getNodeConfig();
592
+ if ((nodeConfig === null || nodeConfig === void 0 ? void 0 : nodeConfig.localizable) === 'categories' &&
593
+ nodeConfig.fromLocalizationFormData) {
594
+ // Get localization data for all categories
595
+ const localizationData = nodeConfig.fromLocalizationFormData(processedFormData, this.node);
596
+ const languageLocalization = ((_b = (_a = this.flowDefinition) === null || _a === void 0 ? void 0 : _a.localization) === null || _b === void 0 ? void 0 : _b[this.languageCode]) || {};
597
+ const categories = ((_d = (_c = this.node) === null || _c === void 0 ? void 0 : _c.router) === null || _d === void 0 ? void 0 : _d.categories) || [];
598
+ categories.forEach((category) => {
599
+ const categoryUuid = category.uuid;
600
+ const nextLocalization = localizationData[categoryUuid];
601
+ if (nextLocalization) {
602
+ this.updateLocalization(this.languageCode, categoryUuid, nextLocalization);
603
+ }
604
+ else if (languageLocalization[categoryUuid]) {
605
+ // Remove existing localization when the translation was cleared
606
+ this.updateLocalization(this.languageCode, categoryUuid, {});
607
+ }
608
+ });
609
+ // Close the dialog
610
+ this.fireCustomEvent(CustomEventType.NodeEditCancelled, {});
611
+ return;
612
+ }
613
+ }
614
+ }
501
615
  // Determine whether to use node or action saving based on context
502
616
  // If we have a node with a router, always use node saving (even if action is set)
503
617
  // because router configuration is handled at the node level
@@ -535,6 +649,12 @@ export class NodeEditor extends RapidElement {
535
649
  });
536
650
  }
537
651
  }
652
+ updateLocalization(languageCode, actionUuid, localizationData) {
653
+ // Use the store method to properly update localization with immer
654
+ zustand
655
+ .getState()
656
+ .updateLocalization(languageCode, actionUuid, localizationData);
657
+ }
538
658
  processFormDataForSave() {
539
659
  const processed = { ...this.formData };
540
660
  // Convert key-value arrays to Records
@@ -586,8 +706,9 @@ export class NodeEditor extends RapidElement {
586
706
  'There was an error creating' + ' "' + selected.name + '"';
587
707
  }
588
708
  }
589
- // Check required fields
590
- if (fieldConfig.required &&
709
+ // Check required fields (skip in localization mode since all fields are optional)
710
+ if (!this.isTranslating &&
711
+ fieldConfig.required &&
591
712
  (!value || (Array.isArray(value) && value.length === 0))) {
592
713
  errors[fieldName] = `${fieldConfig.label || fieldName} is required.`;
593
714
  }
@@ -948,12 +1069,17 @@ export class NodeEditor extends RapidElement {
948
1069
  const containerStyle = config.maxWidth
949
1070
  ? `max-width: ${config.maxWidth};`
950
1071
  : '';
1072
+ // Render original value if in localization mode and action has the field
1073
+ const originalValueDisplay = this.isTranslating && this.action && fieldName in this.action
1074
+ ? this.renderOriginalValue(fieldName, this.action[fieldName])
1075
+ : html ``;
951
1076
  const fieldContent = this.renderFieldContent(fieldName, config, value, errors);
1077
+ const content = html ` ${originalValueDisplay} ${fieldContent} `;
952
1078
  // Wrap in container with style if maxWidth is specified
953
1079
  if (containerStyle) {
954
- return html `<div style="${containerStyle}">${fieldContent}</div>`;
1080
+ return html `<div style="${containerStyle}">${content}</div>`;
955
1081
  }
956
- return fieldContent;
1082
+ return content;
957
1083
  }
958
1084
  renderOptionalField(fieldName, config, value) {
959
1085
  // If the field has a value or has been revealed, show it
@@ -984,9 +1110,76 @@ export class NodeEditor extends RapidElement {
984
1110
  fieldName
985
1111
  ]);
986
1112
  }
1113
+ renderCategoryLocalizationTable() {
1114
+ const categories = this.formData.categories || {};
1115
+ const categoryEntries = Object.entries(categories);
1116
+ if (categoryEntries.length === 0) {
1117
+ return html `<div>No categories to localize</div>`;
1118
+ }
1119
+ const languageName = getStore().getLanguageName(this.languageCode);
1120
+ return html `
1121
+ <div class="category-localization-table">
1122
+ ${categoryEntries.map(([categoryUuid, categoryData]) => html `
1123
+ <div class="category-localization-row">
1124
+ <div class="original-name">${categoryData.originalName}</div>
1125
+ <div class="localized-name">
1126
+ <temba-textinput
1127
+ name="${categoryUuid}"
1128
+ placeholder="${languageName} Translation"
1129
+ value="${categoryData.localizedName || ''}"
1130
+ @change=${(e) => this.handleCategoryLocalizationChange(categoryUuid, e.target.value)}
1131
+ ></temba-textinput>
1132
+ </div>
1133
+ </div>
1134
+ `)}
1135
+ </div>
1136
+ `;
1137
+ }
1138
+ handleCategoryLocalizationChange(categoryUuid, value) {
1139
+ // Update formData with new localized value
1140
+ if (!this.formData.categories) {
1141
+ this.formData.categories = {};
1142
+ }
1143
+ if (!this.formData.categories[categoryUuid]) {
1144
+ this.formData.categories[categoryUuid] = {};
1145
+ }
1146
+ this.formData.categories[categoryUuid].localizedName = value;
1147
+ // Trigger a re-render
1148
+ this.requestUpdate();
1149
+ }
1150
+ renderOriginalValue(fieldName, originalValue) {
1151
+ // Format the original value for display
1152
+ let displayValue = '';
1153
+ if (Array.isArray(originalValue)) {
1154
+ if (originalValue.length === 0) {
1155
+ return html ``; // Don't show anything for empty arrays
1156
+ }
1157
+ // For arrays, join with commas
1158
+ displayValue = originalValue.join(', ');
1159
+ }
1160
+ else if (typeof originalValue === 'string') {
1161
+ displayValue = originalValue;
1162
+ }
1163
+ else if (originalValue) {
1164
+ displayValue = String(originalValue);
1165
+ }
1166
+ // Don't show if empty
1167
+ if (!displayValue || displayValue.trim() === '') {
1168
+ return html ``;
1169
+ }
1170
+ return html `
1171
+ <div class="original-value">
1172
+ <div class="original-value-content">${displayValue}</div>
1173
+ </div>
1174
+ `;
1175
+ }
987
1176
  renderFieldContent(fieldName, config, value, errors) {
1177
+ // In localization mode, make all fields optional (not required)
1178
+ const fieldConfig = this.isTranslating
1179
+ ? { ...config, required: false }
1180
+ : config;
988
1181
  // Use FieldRenderer for consistent field rendering
989
- return FieldRenderer.renderField(fieldName, config, value, {
1182
+ return FieldRenderer.renderField(fieldName, fieldConfig, value, {
990
1183
  errors,
991
1184
  onChange: (e) => {
992
1185
  // Handle different change event types
@@ -1330,6 +1523,12 @@ export class NodeEditor extends RapidElement {
1330
1523
  if (!config) {
1331
1524
  return html ` <div>No configuration available</div> `;
1332
1525
  }
1526
+ // Special rendering for category localization
1527
+ if (this.isTranslating &&
1528
+ config.localizable === 'categories' &&
1529
+ this.formData.categories) {
1530
+ return this.renderCategoryLocalizationTable();
1531
+ }
1333
1532
  // Use the new fields configuration system
1334
1533
  if (config.form) {
1335
1534
  // If layout is specified, use it
@@ -1374,6 +1573,10 @@ export class NodeEditor extends RapidElement {
1374
1573
  if (!(config === null || config === void 0 ? void 0 : config.gutter) || config.gutter.length === 0) {
1375
1574
  return html ``;
1376
1575
  }
1576
+ // Don't show gutter when localizing categories
1577
+ if (this.isTranslating && config.localizable === 'categories') {
1578
+ return html ``;
1579
+ }
1377
1580
  // Use the same layout rendering system for gutter fields
1378
1581
  const renderedFields = new Set();
1379
1582
  return html `
@@ -1443,17 +1646,25 @@ export class NodeEditor extends RapidElement {
1443
1646
  if (!this.isOpen) {
1444
1647
  return html ``;
1445
1648
  }
1446
- const headerColor = this.getHeaderColor();
1649
+ const headerColor = this.isTranslating ? '#505050' : this.getHeaderColor();
1650
+ const headerTextColor = this.isTranslating ? '#fff' : '#fff';
1447
1651
  const config = this.getConfig();
1448
1652
  const dialogSize = (config === null || config === void 0 ? void 0 : config.dialogSize) || 'medium'; // Default to 'large' if not specified
1653
+ const languageName = this.isTranslating
1654
+ ? getStore().getLanguageName(this.languageCode)
1655
+ : '';
1656
+ let header = (config === null || config === void 0 ? void 0 : config.name) || 'Edit';
1657
+ if (this.isTranslating) {
1658
+ header = languageName ? `${languageName} - ${header}` : header;
1659
+ }
1449
1660
  return html `
1450
1661
  <temba-dialog
1451
- header="${(config === null || config === void 0 ? void 0 : config.name) || 'Edit'}"
1662
+ header="${header}"
1452
1663
  .open="${this.isOpen}"
1453
1664
  @temba-button-clicked=${this.handleDialogButtonClick}
1454
1665
  primaryButtonName="Save"
1455
1666
  cancelButtonName="Cancel"
1456
- style="--header-bg: ${headerColor}"
1667
+ style="--header-bg: ${headerColor}; --header-text: ${headerTextColor};"
1457
1668
  size="${dialogSize}"
1458
1669
  >
1459
1670
  <div class="node-editor-form">
@@ -1498,4 +1709,13 @@ __decorate([
1498
1709
  __decorate([
1499
1710
  state()
1500
1711
  ], NodeEditor.prototype, "revealedOptionalFields", void 0);
1712
+ __decorate([
1713
+ fromStore(zustand, (state) => state.languageCode)
1714
+ ], NodeEditor.prototype, "languageCode", void 0);
1715
+ __decorate([
1716
+ fromStore(zustand, (state) => state.isTranslating)
1717
+ ], NodeEditor.prototype, "isTranslating", void 0);
1718
+ __decorate([
1719
+ fromStore(zustand, (state) => state.flowDefinition)
1720
+ ], NodeEditor.prototype, "flowDefinition", void 0);
1501
1721
  //# sourceMappingURL=NodeEditor.js.map