@capillarytech/creatives-library 8.0.254 → 8.0.255-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/assets/Android.png +0 -0
  2. package/assets/iOS.png +0 -0
  3. package/constants/unified.js +2 -1
  4. package/initialReducer.js +2 -0
  5. package/package.json +1 -1
  6. package/services/api.js +10 -0
  7. package/services/tests/api.test.js +34 -0
  8. package/utils/common.js +5 -0
  9. package/utils/commonUtils.js +28 -5
  10. package/utils/tests/commonUtil.test.js +224 -0
  11. package/utils/transformTemplateConfig.js +0 -10
  12. package/v2Components/CapDeviceContent/index.js +61 -56
  13. package/v2Components/CapTagList/index.js +6 -1
  14. package/v2Components/CapTagListWithInput/index.js +5 -1
  15. package/v2Components/CapTagListWithInput/messages.js +1 -1
  16. package/v2Components/CapWhatsappCTA/tests/index.test.js +5 -0
  17. package/v2Components/ErrorInfoNote/constants.js +1 -0
  18. package/v2Components/ErrorInfoNote/index.js +457 -72
  19. package/v2Components/ErrorInfoNote/messages.js +36 -6
  20. package/v2Components/ErrorInfoNote/style.scss +282 -6
  21. package/v2Components/FormBuilder/tests/index.test.js +13 -4
  22. package/v2Components/HtmlEditor/HTMLEditor.js +547 -94
  23. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +874 -0
  24. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1358 -133
  25. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +27 -16
  26. package/v2Components/HtmlEditor/_htmlEditor.scss +108 -45
  27. package/v2Components/HtmlEditor/_index.lazy.scss +0 -1
  28. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +22 -101
  29. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +149 -140
  30. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +2 -1
  31. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
  32. package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +9 -0
  33. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +1 -1
  34. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +22 -0
  35. package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +4 -7
  36. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +35 -45
  37. package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +1 -3
  38. package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
  39. package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +7 -6
  40. package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +3 -6
  41. package/v2Components/HtmlEditor/components/PreviewPane/index.js +24 -34
  42. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  43. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +49 -31
  44. package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +50 -34
  45. package/v2Components/HtmlEditor/components/ValidationPanel/constants.js +6 -0
  46. package/v2Components/HtmlEditor/components/ValidationPanel/index.js +70 -41
  47. package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +254 -0
  48. package/v2Components/HtmlEditor/components/ValidationTabs/index.js +364 -0
  49. package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +51 -0
  50. package/v2Components/HtmlEditor/constants.js +42 -20
  51. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +373 -16
  52. package/v2Components/HtmlEditor/hooks/__tests__/useValidation.apiErrors.test.js +794 -0
  53. package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
  54. package/v2Components/HtmlEditor/hooks/useInAppContent.js +88 -146
  55. package/v2Components/HtmlEditor/hooks/useValidation.js +189 -53
  56. package/v2Components/HtmlEditor/index.js +1 -1
  57. package/v2Components/HtmlEditor/messages.js +95 -85
  58. package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +94 -45
  59. package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +134 -0
  60. package/v2Components/HtmlEditor/utils/contentSanitizer.js +40 -41
  61. package/v2Components/HtmlEditor/utils/htmlValidator.js +71 -72
  62. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +134 -102
  63. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +23 -25
  64. package/v2Components/HtmlEditor/utils/validationAdapter.js +66 -41
  65. package/v2Components/HtmlEditor/utils/validationConstants.js +40 -0
  66. package/v2Components/MobilePushPreviewV2/index.js +32 -7
  67. package/v2Components/TemplatePreview/_templatePreview.scss +55 -24
  68. package/v2Components/TemplatePreview/index.js +47 -32
  69. package/v2Components/TemplatePreview/messages.js +4 -0
  70. package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +1 -0
  71. package/v2Containers/BeeEditor/index.js +172 -90
  72. package/v2Containers/BeePopupEditor/_beePopupEditor.scss +14 -0
  73. package/v2Containers/BeePopupEditor/constants.js +10 -0
  74. package/v2Containers/BeePopupEditor/index.js +194 -0
  75. package/v2Containers/BeePopupEditor/tests/index.test.js +627 -0
  76. package/v2Containers/CreativesContainer/SlideBoxContent.js +128 -51
  77. package/v2Containers/CreativesContainer/SlideBoxFooter.js +163 -13
  78. package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -1
  79. package/v2Containers/CreativesContainer/constants.js +1 -0
  80. package/v2Containers/CreativesContainer/index.js +239 -46
  81. package/v2Containers/CreativesContainer/messages.js +8 -0
  82. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +11 -2
  83. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +38 -50
  84. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +106 -0
  85. package/v2Containers/Email/actions.js +7 -0
  86. package/v2Containers/Email/constants.js +5 -1
  87. package/v2Containers/Email/index.js +234 -29
  88. package/v2Containers/Email/messages.js +32 -0
  89. package/v2Containers/Email/reducer.js +12 -1
  90. package/v2Containers/Email/sagas.js +61 -7
  91. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +2 -0
  92. package/v2Containers/Email/tests/reducer.test.js +46 -0
  93. package/v2Containers/Email/tests/sagas.test.js +320 -29
  94. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1285 -0
  95. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +207 -19
  96. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +40 -74
  97. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +1870 -0
  98. package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +520 -0
  99. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +2 -67
  100. package/v2Containers/EmailWrapper/constants.js +2 -0
  101. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +629 -77
  102. package/v2Containers/EmailWrapper/index.js +103 -23
  103. package/v2Containers/EmailWrapper/messages.js +61 -1
  104. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +643 -0
  105. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +594 -77
  106. package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +376 -0
  107. package/v2Containers/InApp/__tests__/sagas.test.js +363 -0
  108. package/v2Containers/InApp/actions.js +7 -0
  109. package/v2Containers/InApp/constants.js +20 -4
  110. package/v2Containers/InApp/index.js +802 -359
  111. package/v2Containers/InApp/index.scss +4 -3
  112. package/v2Containers/InApp/messages.js +7 -3
  113. package/v2Containers/InApp/reducer.js +21 -3
  114. package/v2Containers/InApp/sagas.js +29 -9
  115. package/v2Containers/InApp/selectors.js +25 -5
  116. package/v2Containers/InApp/tests/index.test.js +154 -50
  117. package/v2Containers/InApp/tests/reducer.test.js +34 -0
  118. package/v2Containers/InApp/tests/sagas.test.js +61 -9
  119. package/v2Containers/InApp/tests/selectors.test.js +612 -0
  120. package/v2Containers/InAppWrapper/components/InAppWrapperView.js +151 -0
  121. package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +267 -0
  122. package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +23 -0
  123. package/v2Containers/InAppWrapper/constants.js +16 -0
  124. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +473 -0
  125. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +198 -0
  126. package/v2Containers/InAppWrapper/index.js +148 -0
  127. package/v2Containers/InAppWrapper/messages.js +49 -0
  128. package/v2Containers/InappAdvance/index.js +1099 -0
  129. package/v2Containers/InappAdvance/index.scss +10 -0
  130. package/v2Containers/InappAdvance/tests/index.test.js +448 -0
  131. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -0
  132. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -0
  133. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -0
  134. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -0
  135. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -0
  136. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -0
  137. package/v2Containers/TagList/index.js +62 -19
  138. package/v2Containers/Templates/_templates.scss +60 -1
  139. package/v2Containers/Templates/index.js +89 -4
  140. package/v2Containers/Templates/messages.js +4 -0
  141. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -0
  142. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +0 -152
  143. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +0 -214
@@ -207,13 +207,31 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
207
207
  const isBEESupport = (this.props.location.query.isBEESupport !== "false") || false;
208
208
  const isBEEAppEnable = this.checkBeeEditorAllowedForLibrary();
209
209
  if (!_.isEmpty(this.props.Templates.BEETemplate)) {
210
- if (this.props.Templates.BEETemplate.versions.base.is_drag_drop && isBEEAppEnable ) {
210
+ const isDragDrop = this.props.Templates.BEETemplate.versions?.base?.is_drag_drop;
211
+
212
+ if (isDragDrop && isBEEAppEnable ) {
211
213
  this.setState({isDragDrop: true});
212
214
  }
213
215
  if (this.props.params.id) {
214
- this.props.actions.getCmsSetting(BEE_PLUGIN, _.get(this.props.Templates.BEETemplate, 'versions.base.drag_drop_id', this.props.Templates.BEETemplate?._id), 'open', undefined, isBEESupport, isBEEAppEnable);
216
+ // Extract drag_drop_id - check multiple possible paths
217
+ const activeTab = this.props.Templates.BEETemplate.versions?.base?.activeTab || 'en';
218
+ const activeTabData = this.props.Templates.BEETemplate.versions?.base?.[activeTab] || {};
219
+ const dragDropId = activeTabData.drag_drop_id
220
+ || activeTabData.id
221
+ || _.get(this.props.Templates.BEETemplate, 'versions.base.drag_drop_id')
222
+ || _.get(this.props.Templates.BEETemplate, 'versions.base.id')
223
+ || this.props.Templates.BEETemplate?._id;
224
+ this.props.actions.getCmsSetting(BEE_PLUGIN, dragDropId, 'open', undefined, isBEESupport, isBEEAppEnable);
215
225
  } else if (this.props.location.query.module !== "library" || (this.props.location.query.module === "library" && !this.props.templateData)) {
216
- this.props.actions.getCmsSetting(BEE_PLUGIN, _.get(this.props.Templates.BEETemplate, 'versions.base.drag_drop_id', this.props.Templates.BEETemplate?._id), 'create', undefined, isBEESupport, isBEEAppEnable);
226
+ // Extract drag_drop_id - check multiple possible paths
227
+ const activeTab = this.props.Templates.BEETemplate.versions?.base?.activeTab || 'en';
228
+ const activeTabData = this.props.Templates.BEETemplate.versions?.base?.[activeTab] || {};
229
+ const dragDropId = activeTabData.drag_drop_id
230
+ || activeTabData.id
231
+ || _.get(this.props.Templates.BEETemplate, 'versions.base.drag_drop_id')
232
+ || _.get(this.props.Templates.BEETemplate, 'versions.base.id')
233
+ || this.props.Templates.BEETemplate?._id;
234
+ this.props.actions.getCmsSetting(BEE_PLUGIN, dragDropId, 'create', undefined, isBEESupport, isBEEAppEnable);
217
235
  }
218
236
  }
219
237
  this.setState({ content: (this.props.Templates.selectedEmailLayout ? this.props.Templates.selectedEmailLayout : ''), formData});
@@ -236,11 +254,52 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
236
254
  if (this.props.location.query.type === 'embedded') {
237
255
  this.showNext();
238
256
  }
257
+
258
+ // Check if BEETemplate was set after componentWillMount but before componentDidMount
259
+ // This can happen if BEETemplate is set asynchronously
260
+ if (!_.isEmpty(this.props.Templates.BEETemplate) && !this.state.isDragDrop) {
261
+ const isBEESupport = (this.props.location.query.isBEESupport !== "false") || false;
262
+ const isBEEAppEnable = this.checkBeeEditorAllowedForLibrary();
263
+ const beeTemplate = this.props.Templates.BEETemplate;
264
+ const activeTab = beeTemplate.versions?.base?.activeTab || 'en';
265
+ const activeTabData = beeTemplate.versions?.base?.[activeTab] || {};
266
+ const isDragDrop = activeTabData.is_drag_drop
267
+ || beeTemplate.versions?.base?.is_drag_drop
268
+ || beeTemplate.base?.is_drag_drop
269
+ || beeTemplate.is_drag_drop;
270
+
271
+ if (isDragDrop && isBEEAppEnable) {
272
+ this.setState({isDragDrop: true});
273
+
274
+ const dragDropId = activeTabData.drag_drop_id
275
+ || activeTabData.id
276
+ || _.get(beeTemplate, 'versions.base.drag_drop_id')
277
+ || _.get(beeTemplate, 'versions.base.id')
278
+ || beeTemplate._id;
279
+
280
+ if (this.props.params.id) {
281
+ const activeTabForLang = beeTemplate.versions?.base?.activeTab || 'en';
282
+ this.props.actions.getCmsSetting(BEE_PLUGIN, dragDropId, 'open', activeTabForLang, isBEESupport, isBEEAppEnable);
283
+ } else {
284
+ this.props.actions.getCmsSetting(BEE_PLUGIN, dragDropId, 'create', undefined, isBEESupport, isBEEAppEnable);
285
+ }
286
+ }
287
+ }
239
288
  }
240
289
 
241
290
 
242
- checkBeeEditorAllowedForLibrary = () => {
243
- const { isFullMode = false, editor } = this.props || {};
291
+ checkBeeEditorAllowedForLibrary = (props = null) => {
292
+ // Allow passing props for use in componentWillReceiveProps (to use nextProps)
293
+ const componentProps = props || this.props;
294
+ const { isFullMode = false, editor, Email } = componentProps || {};
295
+ // IMPORTANT: For isBEEAppEnable API parameter, use API response if available
296
+ // This ensures consistent behavior across full mode and library mode
297
+ // The API response (Email.isBeeEnabled) represents the actual org setting
298
+ if (Email?.isBeeEnabled !== undefined && Email?.isBeeEnabled !== null) {
299
+ return Email.isBeeEnabled;
300
+ }
301
+ // Fallback to mode-based logic for UI purposes (editor selection, etc.)
302
+ // But for API calls, this should ideally use the API response
244
303
  if ((editor === "BEE" && !isFullMode) || isFullMode) {
245
304
  return true;
246
305
  }
@@ -341,9 +400,61 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
341
400
  // this.props.globalActions.fetchSchemaForEntity(query);
342
401
  // }
343
402
 
403
+ // Check if BEETemplate was just set (for new flow when template details load)
404
+ // This handles the case where BEETemplate is set after componentWillMount
405
+ const wasBEETemplateEmpty = _.isEmpty(this.props.Templates.BEETemplate);
406
+ const isBEETemplateNowSet = !_.isEmpty(nextProps.Templates.BEETemplate);
407
+ const isBEEAppEnable = this.checkBeeEditorAllowedForLibrary(nextProps);
408
+ const isBEESupport = (nextProps.location.query.isBEESupport !== "false") || false;
409
+
410
+ if (wasBEETemplateEmpty && isBEETemplateNowSet && isBEEAppEnable) {
411
+ const beeTemplate = nextProps.Templates.BEETemplate;
412
+ const isDragDrop = beeTemplate.versions?.base?.is_drag_drop
413
+ || beeTemplate.versions?.base?.[beeTemplate.versions?.base?.activeTab || 'en']?.is_drag_drop
414
+ || beeTemplate.base?.is_drag_drop
415
+ || beeTemplate.is_drag_drop;
416
+
417
+ // Check if we're in edit mode - check multiple sources for id
418
+ const hasParamsId = nextProps.params?.id
419
+ || nextProps.location?.query?.id
420
+ || nextProps.location?.params?.id
421
+ || (nextProps.location?.pathname && nextProps.location.pathname.includes('/edit/'));
422
+
423
+ if (isDragDrop) {
424
+ this.setState({isDragDrop: true});
425
+
426
+ // Extract drag_drop_id - check multiple possible paths
427
+ // Priority: versions.base[activeTab].drag_drop_id > versions.base[activeTab].id > versions.base.drag_drop_id > _id
428
+ const activeTab = beeTemplate.versions?.base?.activeTab || 'en';
429
+ const activeTabData = beeTemplate.versions?.base?.[activeTab] || {};
430
+ let dragDropId = activeTabData.drag_drop_id
431
+ || activeTabData.id
432
+ || beeTemplate.versions?.base?.drag_drop_id
433
+ || beeTemplate.versions?.base?.id
434
+ || beeTemplate.base?.drag_drop_id
435
+ || beeTemplate.base?.id
436
+ || beeTemplate._id;
437
+
438
+ // Call getCmsSetting for BEE template - use 'open' mode if in edit mode
439
+ // Extract langId from active tab
440
+ const activeTabForLang = beeTemplate.versions?.base?.activeTab || 'en';
441
+ if (hasParamsId) {
442
+ this.props.actions.getCmsSetting(BEE_PLUGIN, dragDropId, 'open', activeTabForLang, isBEESupport, isBEEAppEnable);
443
+ } else if (nextProps.location.query.module !== "library" || (nextProps.location.query.module === "library" && !nextProps.templateData)) {
444
+ this.props.actions.getCmsSetting(BEE_PLUGIN, dragDropId, 'create', undefined, isBEESupport, isBEEAppEnable);
445
+ }
446
+ }
447
+ }
448
+
344
449
  if (this.state.isEdit && !this.state.editDataSet && !_.isEmpty(nextProps.Email.templateDetails) && !_.isEmpty(this.state.schema)) {
345
450
  this.setState({editDataSet: true}, () => {
346
451
  this.setEditData(nextProps.Email.templateDetails);
452
+ // Update template name in parent if available
453
+ if (this.props.isFullMode && this.props.showTemplateName && nextProps.Email.templateDetails.name) {
454
+ const formData = this.state.formData;
455
+ formData['template-name'] = nextProps.Email.templateDetails.name;
456
+ this.props.showTemplateName({formData, onFormDataChange: this.onFormDataChange});
457
+ }
347
458
  });
348
459
  }
349
460
  if (this.state.isEdit && nextProps.location.query.module === "library" && !_.isEmpty(nextProps.templateData) && !this.props.params.id && !nextProps.isGetFormData && _.isEmpty(_.get(this, `state.formData['template-subject']`))) {
@@ -382,7 +493,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
382
493
  const formData = _.cloneDeep(this.state.formData);
383
494
 
384
495
  const schema = _.cloneDeep(this.state.schema);
385
- const langIndex = formData[this.state.currentTab - 1].selectedLanguages.indexOf(langId);
496
+ const langIndex = formData[this.state.currentTab - 1]?.selectedLanguages?.indexOf(langId);
386
497
 
387
498
  const temp = (schema.containers || {})[this.state.currentTab - 1];
388
499
 
@@ -390,32 +501,91 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
390
501
  if (nextProps.Email.CmsSettings.isDragDrop && this.checkBeeEditorAllowedForLibrary()) {
391
502
  const beeJson = `BEEeditor${currentTab > 1 ? currentTab : ''}json`;
392
503
  const beeToken = `BEEeditor${currentTab > 1 ? currentTab : ''}token`;
393
- let beeJsonValue = _.get(nextProps, 'Templates.BEETemplate.versions.base.json-content', '');
504
+ let beeJsonValue = '';
394
505
  const selectedId = _.get(this.props, 'Email.templateDetails._id', '') || _.get(this.props, 'Templates.BEETemplate.versions.base.drag_drop_id', '');
395
- if (this.state.isEdit) {
396
- if (this.props.location.query.module === "library") {
397
- beeJsonValue = _.get(this.state, `formData[${currentTab - 1}][${langId}].json-content`, '');
398
- } else {
399
- beeJsonValue = _.get(nextProps, `Email.templateDetails.versions.base[${langId}].json-content`, '');
506
+
507
+ // Get beeJsonValue from template data - check multiple sources
508
+ // First check if it's already in formData (from setEditData)
509
+ beeJsonValue = _.get(this.state, `formData[${currentTab - 1}][${langId}].json-content`, '');
510
+
511
+ // Also check formData.base
512
+ if (!beeJsonValue) {
513
+ beeJsonValue = _.get(this.state, `formData.base[${langId}].json-content`, '');
514
+ }
515
+
516
+ // Always check templateDetails and BEETemplate regardless of mode
517
+ if (!beeJsonValue) {
518
+ // Check Email.templateDetails first
519
+ const templateDetails = nextProps.Email.templateDetails;
520
+ if (templateDetails && templateDetails.versions && templateDetails.versions.base) {
521
+ const baseVersion = templateDetails.versions.base;
522
+ // Try language-specific path first (e.g., base.en.json-content)
523
+ if (baseVersion[langId] && baseVersion[langId]['json-content']) {
524
+ beeJsonValue = baseVersion[langId]['json-content'];
525
+ } else if (baseVersion['json-content']) {
526
+ // Fallback to base-level json-content
527
+ beeJsonValue = baseVersion['json-content'];
528
+ }
529
+ }
530
+
531
+ // If still not found, check BEETemplate
532
+ if (!beeJsonValue) {
533
+ const beeTemplate = nextProps.Templates.BEETemplate;
534
+ if (beeTemplate && beeTemplate.versions && beeTemplate.versions.base) {
535
+ const beeBase = beeTemplate.versions.base;
536
+ if (beeBase[langId] && beeBase[langId]['json-content']) {
537
+ beeJsonValue = beeBase[langId]['json-content'];
538
+ } else if (beeBase['json-content']) {
539
+ beeJsonValue = beeBase['json-content'];
540
+ }
541
+ }
400
542
  }
401
543
  }
544
+ // Ensure we have a valid beeJsonValue - if it's a string, try to parse it to verify it's valid JSON
545
+ let finalBeeJsonValue = beeJsonValue;
546
+ if (beeJsonValue && typeof beeJsonValue === 'string') {
547
+ try {
548
+ // Try to parse to verify it's valid JSON
549
+ JSON.parse(beeJsonValue);
550
+ } catch (e) {
551
+ console.warn('[Email] componentWillReceiveProps - beeJsonValue is not valid JSON, using as-is:', e);
552
+ }
553
+ }
554
+
555
+ // Preserve existing formData values, especially template-subject
556
+ const existingFormData = formData[`${currentTab - 1}`][langId] || {};
402
557
  formData[`${currentTab - 1}`][langId] = {
403
- ...formData[`${currentTab - 1}`][langId],
558
+ ...existingFormData,
404
559
  is_drag_drop: true,
405
- [beeJson]: beeJsonValue,
560
+ [beeJson]: finalBeeJsonValue,
406
561
  [beeToken]: nextProps.Email.CmsSettings.tokenData,
407
562
  id: selectedId,
563
+ // Also store json-content for reference
564
+ 'json-content': finalBeeJsonValue,
408
565
  };
409
- _.forEach(temp.panes, (pane, index) => {
410
- const tempPane = pane;
411
- //
412
- if (parseInt(index, 10) === parseInt(langIndex, 10)) {
413
- //
414
- tempPane.sections[0].inputFields[0].cols[1].colStyle = {display: ""};
415
- tempPane.sections[0].inputFields[0].cols[0].colStyle = {display: "none"};
566
+
567
+ // Ensure template-subject is preserved at the top level
568
+ if (formData['template-subject'] === undefined || formData['template-subject'] === '') {
569
+ const subjectFromEditData = _.get(nextProps, 'Email.templateDetails.versions.base.subject', '');
570
+ if (subjectFromEditData) {
571
+ formData['template-subject'] = subjectFromEditData;
416
572
  }
417
- });
418
- this.setState({schema, isSchemaChanged: true, loadingStatus: this.state.loadingStatus + 1});
573
+ }
574
+
575
+ if (langIndex !== undefined && langIndex !== -1 && temp && temp.panes) {
576
+ _.forEach(temp.panes, (pane, index) => {
577
+ const tempPane = pane;
578
+ //
579
+ if (parseInt(index, 10) === parseInt(langIndex, 10)) {
580
+ //
581
+ if (tempPane.sections && tempPane.sections[0] && tempPane.sections[0].inputFields && tempPane.sections[0].inputFields[0] && tempPane.sections[0].inputFields[0].cols) {
582
+ tempPane.sections[0].inputFields[0].cols[1].colStyle = {display: ""};
583
+ tempPane.sections[0].inputFields[0].cols[0].colStyle = {display: "none"};
584
+ }
585
+ }
586
+ });
587
+ }
588
+ this.setState({schema, isSchemaChanged: true, loadingStatus: this.state.loadingStatus + 1, formData});
419
589
  } else {
420
590
  _.forEach(temp.panes, (pane, index) => {
421
591
  const tempPane = pane;
@@ -712,8 +882,18 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
712
882
  const msgString = JSON.stringify(msg);
713
883
  const langId = formData[this.state.currentTab - 1].activeTab;
714
884
  const langIndex = formData[this.state.currentTab - 1].selectedLanguages.indexOf(langId);
715
- const win = document.getElementById(`edmeditor${(langIndex + 1) > 1 ? (langIndex + 1) : ''}`).contentWindow;
716
- win.postMessage(msgString, '*');
885
+ const elementToSelect = document.getElementById(`edmeditor${(langIndex + 1) > 1 ? (langIndex + 1) : ''}`);
886
+ if (elementToSelect) {
887
+ try {
888
+ const win = elementToSelect.contentWindow;
889
+ if (win) {
890
+ win.postMessage(msgString, '*');
891
+ }
892
+ } catch (error) {
893
+ // Handle cross-origin frame access errors
894
+ console.warn('Failed to access iframe contentWindow (cross-origin restriction):', error);
895
+ }
896
+ }
717
897
  } else if (data === "unsubscribe" && this.state.editorInstanse) {
718
898
  const anchor = `<a href='{{${data}}}'>${data}</a>`;
719
899
  this.state.editorInstanse.insertHtml(`${anchor}`);
@@ -894,6 +1074,11 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
894
1074
  const isBEESupport = (this.props.location.query.isBEESupport !== "false") || false;
895
1075
  const formData = _.cloneDeep(this.state.formData);
896
1076
 
1077
+ // Set template name from editData if available
1078
+ if (editData.name) {
1079
+ formData['template-name'] = editData.name;
1080
+ }
1081
+
897
1082
  const schema = (this.props.location.query.type === 'embedded') ? this.removeStandAlone(this.state.schema) : _.cloneDeep(this.state.schema);
898
1083
  const containers = _.cloneDeep(schema.containers.slice());
899
1084
  let tabKey = "";
@@ -902,7 +1087,8 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
902
1087
  const type = this.props.location.query.type;
903
1088
 
904
1089
  formData['template-name'] = editData.name;
905
- formData['template-subject'] = _.get(editData, 'versions.base.subject');
1090
+ const subject = _.get(editData, 'versions.base.subject', '');
1091
+ formData['template-subject'] = subject;
906
1092
  formData.base = editData.versions.base;
907
1093
 
908
1094
  _.forEach(editData.versions.history, (data1, index) => {
@@ -983,8 +1169,13 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
983
1169
 
984
1170
 
985
1171
  this.setState({ formData, schema, tabKey, currentTab, tabCount: editData.versions.history.length, loadingStatus: this.state.loadingStatus + 1 }, () => {
986
- if (this.props.isFullMode) {
987
- this.props.showTemplateName({formData, onFormDataChange: this.onFormDataChange});
1172
+ if (this.props.isFullMode && this.props.showTemplateName) {
1173
+ // Ensure template name is set before showing it
1174
+ const updatedFormData = _.cloneDeep(formData);
1175
+ if (editData.name && !updatedFormData['template-name']) {
1176
+ updatedFormData['template-name'] = editData.name;
1177
+ }
1178
+ this.props.showTemplateName({formData: updatedFormData, onFormDataChange: this.onFormDataChange});
988
1179
  }
989
1180
  const isBEEAppEnable = this.checkBeeEditorAllowedForLibrary();
990
1181
  _.forEach((editData.versions.base.selectedLanguages), (language) => {
@@ -2659,6 +2850,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
2659
2850
  getTemplateDetailsInProgress,
2660
2851
  assetUploading,
2661
2852
  createTemplateInProgress,
2853
+ fetchingCmsSettings,
2662
2854
  } = this.props.Email;
2663
2855
  let isLoading =
2664
2856
  isLoadingMetaEntities ||
@@ -2667,7 +2859,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
2667
2859
  ) || (
2668
2860
  assetUploading !== undefined && assetUploading
2669
2861
  ) || (
2670
- Email && (fetchingCmsData || getTemplateDetailsInProgress)
2862
+ Email && (fetchingCmsData || getTemplateDetailsInProgress || fetchingCmsSettings)
2671
2863
  );
2672
2864
 
2673
2865
  if (!isLoading) {
@@ -2989,6 +3181,19 @@ function mapDispatchToProps(dispatch) {
2989
3181
  const withReducer = injectReducer({ key: 'email', reducer: v2EmailReducer });
2990
3182
  const withEmailSaga = injectSaga({ key: 'email', saga: v2EmailSagas });
2991
3183
 
3184
+ // Base Email component without saga registration (for use from EmailWrapper)
3185
+ // EmailWrapper already registers the saga, so we don't need to register it here
3186
+ export const EmailWithoutSaga = withCreatives({
3187
+ WrappedComponent: Email,
3188
+ mapStateToProps,
3189
+ mapDispatchToProps,
3190
+ userAuth: true,
3191
+ sagas: [], // No saga - EmailWrapper registers it
3192
+ reducers: [withReducer],
3193
+ });
3194
+
3195
+ // Email component with saga registration (for direct use from SlideBoxContent in Edit mode)
3196
+ // When Email is used directly (not as child of EmailWrapper), it needs to register the saga
2992
3197
  export default withCreatives({
2993
3198
  WrappedComponent: Email,
2994
3199
  mapStateToProps,
@@ -302,4 +302,36 @@ export default defineMessages({
302
302
  id: 'creatives.containersV2.Email.base64ImageError',
303
303
  defaultMessage: 'Base64 images are not allowed. Please upload your image to a gallery and use it, or add the image URL instead.',
304
304
  },
305
+ "editorTypeTitle": {
306
+ id: 'creatives.containersV2.Email.editorTypeTitle',
307
+ defaultMessage: 'Editor type',
308
+ },
309
+ "htmlEditorTitle": {
310
+ id: 'creatives.containersV2.Email.htmlEditorTitle',
311
+ defaultMessage: 'HTML editor',
312
+ },
313
+ "htmlEditorDescription": {
314
+ id: 'creatives.containersV2.Email.htmlEditorDescription',
315
+ defaultMessage: 'Use a basic HTML editor to write and format your content. Suitable if you are familiar with HTML.',
316
+ },
317
+ "dragDropEditorTitle": {
318
+ id: 'creatives.containersV2.Email.dragDropEditorTitle',
319
+ defaultMessage: 'Drag & drop editor',
320
+ },
321
+ "dragDropEditorDescription": {
322
+ id: 'creatives.containersV2.Email.dragDropEditorDescription',
323
+ defaultMessage: 'Create your content visually by dragging blocks — no coding needed. Great for quick, easy designs.',
324
+ },
325
+ "uploadZipTitle": {
326
+ id: 'creatives.containersV2.Email.uploadZipTitle',
327
+ defaultMessage: 'Upload zip file',
328
+ },
329
+ "uploadZipDescription": {
330
+ id: 'creatives.containersV2.Email.uploadZipDescription',
331
+ defaultMessage: 'Upload a ZIP containing your custom HTML, images, and assets. Ideal if your content is already built.',
332
+ },
333
+ "nextButton": {
334
+ id: 'creatives.containersV2.Email.nextButton',
335
+ defaultMessage: 'Next',
336
+ },
305
337
  });
@@ -11,6 +11,7 @@ import * as types from './constants';
11
11
  const initialState = fromJS({
12
12
  createTemplateInProgress: false,
13
13
  createResponse: {},
14
+ isBeeEnabled: false,
14
15
  });
15
16
 
16
17
  function emailReducer(state = initialState, action) {
@@ -107,6 +108,15 @@ function emailReducer(state = initialState, action) {
107
108
  return state
108
109
  .set('fetchingCmsData', false)
109
110
  .set('fetchingCmsDataFailed', true);
111
+ case types.GET_CMS_ACCOUNTS_REQUEST:
112
+ return state
113
+ .set('isBeeEnabled', false); // default to false
114
+ case types.GET_CMS_ACCOUNTS_SUCCESS:
115
+ return state
116
+ .set('isBeeEnabled', action.isBeeEnabled);
117
+ case types.GET_CMS_ACCOUNTS_FAILURE:
118
+ return state
119
+ .set('isBeeEnabled', false);
110
120
  case types.CLEAR_EMAIL_CRUD_RESPONSE_REQUEST:
111
121
  return state
112
122
  .set('createResponse', fromJS({}));
@@ -139,7 +149,8 @@ function emailReducer(state = initialState, action) {
139
149
  .set('CmsSettings', fromJS({}))
140
150
  .set('fetchingCmsData', false)
141
151
  .set('duplicateResponse', fromJS({}))
142
- .set('cmsData', '');
152
+ .set('cmsData', '')
153
+ .set('isBeeEnabled', false);
143
154
  case types.TRANSFORM_EMAIL_TEMPLATE_REQUEST:
144
155
  return state.set("createTemplateInProgress", true);
145
156
  default:
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  call, put, takeLatest, takeEvery, all,
3
3
  } from 'redux-saga/effects';
4
+ import CapNotification from '@capillarytech/cap-ui-library/CapNotification';
4
5
  import * as Api from '../../services/api';
5
6
  import * as types from './constants';
6
7
  import { transformEmailTemplates, storeS3FileSizeDetails } from '../../utils/cdnTransformation';
@@ -9,15 +10,47 @@ export function* createTemplate(template) {
9
10
  let errorMsg;
10
11
  try {
11
12
  const result = yield call(Api.createEmailTemplate, template);
12
- if (!result.success || result.status.code === 500) {
13
- errorMsg = result.message;
13
+ // Check if the API call failed (non-2xx status codes or explicit failure)
14
+ const statusCode = result.status?.code || result.status;
15
+ const isError = !result.success || statusCode >= 400;
16
+
17
+ if (isError) {
18
+ // Extract error message from various possible locations in the response
19
+ errorMsg = result.message
20
+ || result.errorMessage
21
+ || result.error?.message
22
+ || (result.response && result.response.message)
23
+ || (result.status && result.status.message)
24
+ || (result.data && result.data.message)
25
+ || `API Error: ${statusCode || 'Unknown error'}`;
26
+ // Show error notification directly from saga
27
+ CapNotification.error({
28
+ key: 'createTemplateError',
29
+ message: errorMsg,
30
+ });
14
31
  yield put({ type: types.CREATE_TEMPLATE_FAILURE, errorMsg });
32
+ // Call callback with error so component can handle it (if needed)
33
+ if (template.onCreateTemplateComplete) {
34
+ try {
35
+ // Call the callback directly (it's a regular function, not a generator)
36
+ template.onCreateTemplateComplete({ error: true, message: errorMsg, statusCode });
37
+ } catch (callbackError) {
38
+ console.error('[Email Saga] Error executing callback:', callbackError);
39
+ }
40
+ }
15
41
  } else {
16
- yield put({ type: types.CREATE_TEMPLATE_SUCCESS, data: result.response, statusCode: result.status ? result.status.code : '', errorMsg });
42
+ yield put({
43
+ type: types.CREATE_TEMPLATE_SUCCESS, data: result.response, statusCode: result.status ? result.status.code : '', errorMsg,
44
+ });
17
45
  yield template.onCreateTemplateComplete(result.response);
18
46
  }
19
47
  } catch (error) {
48
+ errorMsg = error.message || error.toString() || 'An unexpected error occurred';
20
49
  yield put({ type: types.CREATE_TEMPLATE_FAILURE, error, errorMsg });
50
+ // Call callback with error so component can handle it
51
+ if (template.onCreateTemplateComplete) {
52
+ yield template.onCreateTemplateComplete({ error: true, message: errorMsg });
53
+ }
21
54
  }
22
55
  }
23
56
 
@@ -26,14 +59,14 @@ export function* transformEmailTemplate({template, callback}) {
26
59
  const result = yield call(transformEmailTemplates, template);
27
60
  yield callback(result);
28
61
  } catch (error) {
29
- yield callback(template);
62
+ yield callback(template);
30
63
  }
31
64
  }
32
65
 
33
66
  export function* duplicateTemplate(payload) {
34
67
  let errorMsg;
35
68
  let result;
36
- let { id, channel, callback } = payload;
69
+ const { id, channel, callback } = payload;
37
70
  try {
38
71
  result = yield call(Api.duplicateTemplate, {id, channel} );
39
72
  if (result.status.code === 500) {
@@ -42,7 +75,9 @@ export function* duplicateTemplate(payload) {
42
75
  if (callback) {
43
76
  callback(result?.response);
44
77
  }
45
- yield put({ type: types.DUPLICATE_TEMPLATE_SUCCESS, data: result?.response, statusCode: result?.status?.code, errorMsg });
78
+ yield put({
79
+ type: types.DUPLICATE_TEMPLATE_SUCCESS, data: result?.response, statusCode: result?.status?.code, errorMsg,
80
+ });
46
81
  } catch (error) {
47
82
  yield put({ type: types.DUPLICATE_TEMPLATE_FAILURE, error, errorMsg });
48
83
  }
@@ -67,7 +102,9 @@ export function* getAllAssets(assetType, queryParams) {
67
102
  }
68
103
  }
69
104
 
70
- export function* getCmsSetting({cmsType, projectId, cmsMode, langId, isEdmSupport, isBEEAppEnable}) {
105
+ export function* getCmsSetting({
106
+ cmsType, projectId, cmsMode, langId, isEdmSupport, isBEEAppEnable,
107
+ }) {
71
108
  try {
72
109
  const result = yield call(Api.getCmsTemplateSettingsV2, cmsType, projectId, cmsMode, langId, isEdmSupport, isBEEAppEnable);
73
110
 
@@ -87,6 +124,17 @@ export function* getCmsData({cmsType, projectId, langId}) {
87
124
  }
88
125
  }
89
126
 
127
+ export function* getCmsAccounts({cmsType}) {
128
+ try {
129
+ const result = yield call(Api.getCmsAccounts, cmsType);
130
+ const { cmsAccounts } = result.data?.response || {};
131
+ const isBeeEnabled = cmsAccounts?.type === cmsType;
132
+ yield put({ type: types.GET_CMS_ACCOUNTS_SUCCESS, isBeeEnabled });
133
+ } catch (error) {
134
+ yield put({ type: types.GET_CMS_ACCOUNTS_FAILURE, error });
135
+ }
136
+ }
137
+
90
138
  export function* uploadAsset(file, assetType, fileParams) {
91
139
  try {
92
140
  const result = yield call(Api.uploadFile, file, assetType, fileParams);
@@ -123,6 +171,10 @@ function* watchGetCmsData() {
123
171
  yield takeEvery(types.GET_CMS_EDITOR_DATA_REQUEST, getCmsData);
124
172
  }
125
173
 
174
+ function* watchGetCmsAccounts() {
175
+ yield takeEvery(types.GET_CMS_ACCOUNTS_REQUEST, getCmsAccounts);
176
+ }
177
+
126
178
  function* watchUploadAsset() {
127
179
  yield takeLatest(types.UPLOAD_ASSET_REQUEST, uploadAsset);
128
180
  }
@@ -139,6 +191,7 @@ export default [
139
191
  watchGetAllAssets,
140
192
  watchGetCmsSetting,
141
193
  watchGetCmsData,
194
+ watchGetCmsAccounts,
142
195
  watchUploadAsset,
143
196
  watchDuplicateTemplate,
144
197
  ];
@@ -151,6 +204,7 @@ export function* v2EmailSagas() {
151
204
  watchGetAllAssets(),
152
205
  watchGetCmsSetting(),
153
206
  watchGetCmsData(),
207
+ watchGetCmsAccounts(),
154
208
  watchUploadAsset(),
155
209
  ]);
156
210
  }
@@ -4,6 +4,7 @@ exports[` 1`] = `
4
4
  Immutable.Map {
5
5
  "createTemplateInProgress": false,
6
6
  "createResponse": Immutable.Map {},
7
+ "isBeeEnabled": false,
7
8
  }
8
9
  `;
9
10
 
@@ -11,5 +12,6 @@ exports[` 2`] = `
11
12
  Immutable.Map {
12
13
  "createTemplateInProgress": true,
13
14
  "createResponse": Immutable.Map {},
15
+ "isBeeEnabled": false,
14
16
  }
15
17
  `;
@@ -15,4 +15,50 @@ describe('emailReducer', () => {
15
15
  };
16
16
  expect(emailReducer(undefined, action)).toMatchSnapshot();
17
17
  });
18
+
19
+ it.concurrent('it handles GET_CMS_ACCOUNTS_REQUEST action (line 111-113)', () => {
20
+ const initialState = fromJS({
21
+ isBeeEnabled: true, // Start with true to verify it gets set to false
22
+ });
23
+ const action = {
24
+ type: types.GET_CMS_ACCOUNTS_REQUEST,
25
+ };
26
+ const result = emailReducer(initialState, action);
27
+ expect(result.get('isBeeEnabled')).toBe(false);
28
+ });
29
+
30
+ it.concurrent('it handles GET_CMS_ACCOUNTS_SUCCESS action (line 114-116)', () => {
31
+ const initialState = fromJS({
32
+ isBeeEnabled: false,
33
+ });
34
+ const action = {
35
+ type: types.GET_CMS_ACCOUNTS_SUCCESS,
36
+ isBeeEnabled: true,
37
+ };
38
+ const result = emailReducer(initialState, action);
39
+ expect(result.get('isBeeEnabled')).toBe(true);
40
+ });
41
+
42
+ it.concurrent('it handles GET_CMS_ACCOUNTS_SUCCESS action with false (line 114-116)', () => {
43
+ const initialState = fromJS({
44
+ isBeeEnabled: true,
45
+ });
46
+ const action = {
47
+ type: types.GET_CMS_ACCOUNTS_SUCCESS,
48
+ isBeeEnabled: false,
49
+ };
50
+ const result = emailReducer(initialState, action);
51
+ expect(result.get('isBeeEnabled')).toBe(false);
52
+ });
53
+
54
+ it.concurrent('it handles GET_CMS_ACCOUNTS_FAILURE action (line 117-119)', () => {
55
+ const initialState = fromJS({
56
+ isBeeEnabled: true, // Start with true to verify it gets set to false
57
+ });
58
+ const action = {
59
+ type: types.GET_CMS_ACCOUNTS_FAILURE,
60
+ };
61
+ const result = emailReducer(initialState, action);
62
+ expect(result.get('isBeeEnabled')).toBe(false);
63
+ });
18
64
  });