@capillarytech/creatives-library 8.0.249 → 8.0.250-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 (136) 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 +18 -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/index.js +452 -72
  18. package/v2Components/ErrorInfoNote/messages.js +22 -0
  19. package/v2Components/ErrorInfoNote/style.scss +280 -4
  20. package/v2Components/FormBuilder/tests/index.test.js +13 -4
  21. package/v2Components/HtmlEditor/HTMLEditor.js +640 -94
  22. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +874 -0
  23. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1167 -133
  24. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +27 -16
  25. package/v2Components/HtmlEditor/_htmlEditor.scss +108 -45
  26. package/v2Components/HtmlEditor/_index.lazy.scss +1 -1
  27. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +13 -101
  28. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +148 -139
  29. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +2 -1
  30. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
  31. package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +9 -0
  32. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +1 -1
  33. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +22 -0
  34. package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +4 -7
  35. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +35 -45
  36. package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +1 -3
  37. package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
  38. package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +7 -6
  39. package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +3 -6
  40. package/v2Components/HtmlEditor/components/PreviewPane/index.js +11 -13
  41. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  42. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +49 -31
  43. package/v2Components/HtmlEditor/components/ValidationPanel/index.js +68 -39
  44. package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +254 -0
  45. package/v2Components/HtmlEditor/components/ValidationTabs/index.js +391 -0
  46. package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +51 -0
  47. package/v2Components/HtmlEditor/constants.js +42 -20
  48. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +373 -16
  49. package/v2Components/HtmlEditor/hooks/__tests__/useValidation.apiErrors.test.js +795 -0
  50. package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
  51. package/v2Components/HtmlEditor/hooks/useInAppContent.js +88 -146
  52. package/v2Components/HtmlEditor/hooks/useValidation.js +189 -53
  53. package/v2Components/HtmlEditor/index.js +1 -1
  54. package/v2Components/HtmlEditor/messages.js +95 -85
  55. package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +94 -45
  56. package/v2Components/HtmlEditor/utils/contentSanitizer.js +40 -41
  57. package/v2Components/HtmlEditor/utils/htmlValidator.js +71 -72
  58. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +134 -102
  59. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +23 -25
  60. package/v2Components/HtmlEditor/utils/validationAdapter.js +66 -41
  61. package/v2Components/MobilePushPreviewV2/index.js +32 -7
  62. package/v2Components/TemplatePreview/_templatePreview.scss +44 -24
  63. package/v2Components/TemplatePreview/index.js +47 -32
  64. package/v2Components/TemplatePreview/messages.js +4 -0
  65. package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +1 -0
  66. package/v2Containers/BeeEditor/index.js +172 -90
  67. package/v2Containers/BeePopupEditor/constants.js +10 -0
  68. package/v2Containers/BeePopupEditor/index.js +193 -0
  69. package/v2Containers/BeePopupEditor/tests/index.test.js +627 -0
  70. package/v2Containers/CreativesContainer/SlideBoxContent.js +127 -51
  71. package/v2Containers/CreativesContainer/SlideBoxFooter.js +163 -13
  72. package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -1
  73. package/v2Containers/CreativesContainer/constants.js +1 -0
  74. package/v2Containers/CreativesContainer/index.js +239 -46
  75. package/v2Containers/CreativesContainer/messages.js +8 -0
  76. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +11 -2
  77. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +38 -50
  78. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +106 -0
  79. package/v2Containers/Email/actions.js +7 -0
  80. package/v2Containers/Email/constants.js +5 -1
  81. package/v2Containers/Email/index.js +222 -27
  82. package/v2Containers/Email/messages.js +32 -0
  83. package/v2Containers/Email/reducer.js +12 -1
  84. package/v2Containers/Email/sagas.js +61 -7
  85. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +2 -0
  86. package/v2Containers/Email/tests/sagas.test.js +320 -29
  87. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1321 -0
  88. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +210 -15
  89. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +40 -74
  90. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +1749 -0
  91. package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +520 -0
  92. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +2 -67
  93. package/v2Containers/EmailWrapper/constants.js +2 -0
  94. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +629 -77
  95. package/v2Containers/EmailWrapper/index.js +103 -23
  96. package/v2Containers/EmailWrapper/messages.js +61 -1
  97. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +643 -0
  98. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +594 -77
  99. package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +376 -0
  100. package/v2Containers/InApp/__tests__/sagas.test.js +363 -0
  101. package/v2Containers/InApp/actions.js +7 -0
  102. package/v2Containers/InApp/constants.js +20 -4
  103. package/v2Containers/InApp/index.js +802 -359
  104. package/v2Containers/InApp/index.scss +4 -3
  105. package/v2Containers/InApp/messages.js +7 -3
  106. package/v2Containers/InApp/reducer.js +21 -3
  107. package/v2Containers/InApp/sagas.js +29 -9
  108. package/v2Containers/InApp/selectors.js +25 -5
  109. package/v2Containers/InApp/tests/index.test.js +154 -50
  110. package/v2Containers/InApp/tests/reducer.test.js +34 -0
  111. package/v2Containers/InApp/tests/sagas.test.js +61 -9
  112. package/v2Containers/InApp/tests/selectors.test.js +612 -0
  113. package/v2Containers/InAppWrapper/components/InAppWrapperView.js +162 -0
  114. package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +267 -0
  115. package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +9 -0
  116. package/v2Containers/InAppWrapper/constants.js +16 -0
  117. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +473 -0
  118. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +198 -0
  119. package/v2Containers/InAppWrapper/index.js +148 -0
  120. package/v2Containers/InAppWrapper/messages.js +49 -0
  121. package/v2Containers/InappAdvance/index.js +1099 -0
  122. package/v2Containers/InappAdvance/index.scss +10 -0
  123. package/v2Containers/InappAdvance/tests/index.test.js +448 -0
  124. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -0
  125. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -0
  126. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -0
  127. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -0
  128. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -0
  129. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -0
  130. package/v2Containers/TagList/index.js +62 -19
  131. package/v2Containers/Templates/_templates.scss +60 -1
  132. package/v2Containers/Templates/index.js +89 -4
  133. package/v2Containers/Templates/messages.js +4 -0
  134. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -0
  135. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +0 -152
  136. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +0 -214
@@ -208,13 +208,31 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
208
208
  const isBEESupport = (this.props.location.query.isBEESupport !== "false") || false;
209
209
  const isBEEAppEnable = this.checkBeeEditorAllowedForLibrary();
210
210
  if (!_.isEmpty(this.props.Templates.BEETemplate)) {
211
- if (this.props.Templates.BEETemplate.versions.base.is_drag_drop && isBEEAppEnable ) {
211
+ const isDragDrop = this.props.Templates.BEETemplate.versions?.base?.is_drag_drop;
212
+
213
+ if (isDragDrop && isBEEAppEnable ) {
212
214
  this.setState({isDragDrop: true});
213
215
  }
214
216
  if (this.props.params.id) {
215
- 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);
217
+ // Extract drag_drop_id - check multiple possible paths
218
+ const activeTab = this.props.Templates.BEETemplate.versions?.base?.activeTab || 'en';
219
+ const activeTabData = this.props.Templates.BEETemplate.versions?.base?.[activeTab] || {};
220
+ const dragDropId = activeTabData.drag_drop_id
221
+ || activeTabData.id
222
+ || _.get(this.props.Templates.BEETemplate, 'versions.base.drag_drop_id')
223
+ || _.get(this.props.Templates.BEETemplate, 'versions.base.id')
224
+ || this.props.Templates.BEETemplate?._id;
225
+ this.props.actions.getCmsSetting(BEE_PLUGIN, dragDropId, 'open', undefined, isBEESupport, isBEEAppEnable);
216
226
  } else if (this.props.location.query.module !== "library" || (this.props.location.query.module === "library" && !this.props.templateData)) {
217
- 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);
227
+ // Extract drag_drop_id - check multiple possible paths
228
+ const activeTab = this.props.Templates.BEETemplate.versions?.base?.activeTab || 'en';
229
+ const activeTabData = this.props.Templates.BEETemplate.versions?.base?.[activeTab] || {};
230
+ const dragDropId = activeTabData.drag_drop_id
231
+ || activeTabData.id
232
+ || _.get(this.props.Templates.BEETemplate, 'versions.base.drag_drop_id')
233
+ || _.get(this.props.Templates.BEETemplate, 'versions.base.id')
234
+ || this.props.Templates.BEETemplate?._id;
235
+ this.props.actions.getCmsSetting(BEE_PLUGIN, dragDropId, 'create', undefined, isBEESupport, isBEEAppEnable);
218
236
  }
219
237
  }
220
238
  this.setState({ content: (this.props.Templates.selectedEmailLayout ? this.props.Templates.selectedEmailLayout : ''), formData});
@@ -237,11 +255,52 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
237
255
  if (this.props.location.query.type === 'embedded') {
238
256
  this.showNext();
239
257
  }
258
+
259
+ // Check if BEETemplate was set after componentWillMount but before componentDidMount
260
+ // This can happen if BEETemplate is set asynchronously
261
+ if (!_.isEmpty(this.props.Templates.BEETemplate) && !this.state.isDragDrop) {
262
+ const isBEESupport = (this.props.location.query.isBEESupport !== "false") || false;
263
+ const isBEEAppEnable = this.checkBeeEditorAllowedForLibrary();
264
+ const beeTemplate = this.props.Templates.BEETemplate;
265
+ const activeTab = beeTemplate.versions?.base?.activeTab || 'en';
266
+ const activeTabData = beeTemplate.versions?.base?.[activeTab] || {};
267
+ const isDragDrop = activeTabData.is_drag_drop
268
+ || beeTemplate.versions?.base?.is_drag_drop
269
+ || beeTemplate.base?.is_drag_drop
270
+ || beeTemplate.is_drag_drop;
271
+
272
+ if (isDragDrop && isBEEAppEnable) {
273
+ this.setState({isDragDrop: true});
274
+
275
+ const dragDropId = activeTabData.drag_drop_id
276
+ || activeTabData.id
277
+ || _.get(beeTemplate, 'versions.base.drag_drop_id')
278
+ || _.get(beeTemplate, 'versions.base.id')
279
+ || beeTemplate._id;
280
+
281
+ if (this.props.params.id) {
282
+ const activeTabForLang = beeTemplate.versions?.base?.activeTab || 'en';
283
+ this.props.actions.getCmsSetting(BEE_PLUGIN, dragDropId, 'open', activeTabForLang, isBEESupport, isBEEAppEnable);
284
+ } else {
285
+ this.props.actions.getCmsSetting(BEE_PLUGIN, dragDropId, 'create', undefined, isBEESupport, isBEEAppEnable);
286
+ }
287
+ }
288
+ }
240
289
  }
241
290
 
242
291
 
243
- checkBeeEditorAllowedForLibrary = () => {
244
- const { isFullMode = false, editor } = this.props || {};
292
+ checkBeeEditorAllowedForLibrary = (props = null) => {
293
+ // Allow passing props for use in componentWillReceiveProps (to use nextProps)
294
+ const componentProps = props || this.props;
295
+ const { isFullMode = false, editor, Email } = componentProps || {};
296
+ // IMPORTANT: For isBEEAppEnable API parameter, use API response if available
297
+ // This ensures consistent behavior across full mode and library mode
298
+ // The API response (Email.isBeeEnabled) represents the actual org setting
299
+ if (Email?.isBeeEnabled !== undefined && Email?.isBeeEnabled !== null) {
300
+ return Email.isBeeEnabled;
301
+ }
302
+ // Fallback to mode-based logic for UI purposes (editor selection, etc.)
303
+ // But for API calls, this should ideally use the API response
245
304
  if ((editor === "BEE" && !isFullMode) || isFullMode) {
246
305
  return true;
247
306
  }
@@ -342,9 +401,61 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
342
401
  // this.props.globalActions.fetchSchemaForEntity(query);
343
402
  // }
344
403
 
404
+ // Check if BEETemplate was just set (for new flow when template details load)
405
+ // This handles the case where BEETemplate is set after componentWillMount
406
+ const wasBEETemplateEmpty = _.isEmpty(this.props.Templates.BEETemplate);
407
+ const isBEETemplateNowSet = !_.isEmpty(nextProps.Templates.BEETemplate);
408
+ const isBEEAppEnable = this.checkBeeEditorAllowedForLibrary(nextProps);
409
+ const isBEESupport = (nextProps.location.query.isBEESupport !== "false") || false;
410
+
411
+ if (wasBEETemplateEmpty && isBEETemplateNowSet && isBEEAppEnable) {
412
+ const beeTemplate = nextProps.Templates.BEETemplate;
413
+ const isDragDrop = beeTemplate.versions?.base?.is_drag_drop
414
+ || beeTemplate.versions?.base?.[beeTemplate.versions?.base?.activeTab || 'en']?.is_drag_drop
415
+ || beeTemplate.base?.is_drag_drop
416
+ || beeTemplate.is_drag_drop;
417
+
418
+ // Check if we're in edit mode - check multiple sources for id
419
+ const hasParamsId = nextProps.params?.id
420
+ || nextProps.location?.query?.id
421
+ || nextProps.location?.params?.id
422
+ || (nextProps.location?.pathname && nextProps.location.pathname.includes('/edit/'));
423
+
424
+ if (isDragDrop) {
425
+ this.setState({isDragDrop: true});
426
+
427
+ // Extract drag_drop_id - check multiple possible paths
428
+ // Priority: versions.base[activeTab].drag_drop_id > versions.base[activeTab].id > versions.base.drag_drop_id > _id
429
+ const activeTab = beeTemplate.versions?.base?.activeTab || 'en';
430
+ const activeTabData = beeTemplate.versions?.base?.[activeTab] || {};
431
+ let dragDropId = activeTabData.drag_drop_id
432
+ || activeTabData.id
433
+ || beeTemplate.versions?.base?.drag_drop_id
434
+ || beeTemplate.versions?.base?.id
435
+ || beeTemplate.base?.drag_drop_id
436
+ || beeTemplate.base?.id
437
+ || beeTemplate._id;
438
+
439
+ // Call getCmsSetting for BEE template - use 'open' mode if in edit mode
440
+ // Extract langId from active tab
441
+ const activeTabForLang = beeTemplate.versions?.base?.activeTab || 'en';
442
+ if (hasParamsId) {
443
+ this.props.actions.getCmsSetting(BEE_PLUGIN, dragDropId, 'open', activeTabForLang, isBEESupport, isBEEAppEnable);
444
+ } else if (nextProps.location.query.module !== "library" || (nextProps.location.query.module === "library" && !nextProps.templateData)) {
445
+ this.props.actions.getCmsSetting(BEE_PLUGIN, dragDropId, 'create', undefined, isBEESupport, isBEEAppEnable);
446
+ }
447
+ }
448
+ }
449
+
345
450
  if (this.state.isEdit && !this.state.editDataSet && !_.isEmpty(nextProps.Email.templateDetails) && !_.isEmpty(this.state.schema)) {
346
451
  this.setState({editDataSet: true}, () => {
347
452
  this.setEditData(nextProps.Email.templateDetails);
453
+ // Update template name in parent if available
454
+ if (this.props.isFullMode && this.props.showTemplateName && nextProps.Email.templateDetails.name) {
455
+ const formData = this.state.formData;
456
+ formData['template-name'] = nextProps.Email.templateDetails.name;
457
+ this.props.showTemplateName({formData, onFormDataChange: this.onFormDataChange});
458
+ }
348
459
  });
349
460
  }
350
461
  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']`))) {
@@ -383,7 +494,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
383
494
  const formData = _.cloneDeep(this.state.formData);
384
495
 
385
496
  const schema = _.cloneDeep(this.state.schema);
386
- const langIndex = formData[this.state.currentTab - 1].selectedLanguages.indexOf(langId);
497
+ const langIndex = formData[this.state.currentTab - 1]?.selectedLanguages?.indexOf(langId);
387
498
 
388
499
  const temp = (schema.containers || {})[this.state.currentTab - 1];
389
500
 
@@ -391,32 +502,91 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
391
502
  if (nextProps.Email.CmsSettings.isDragDrop && this.checkBeeEditorAllowedForLibrary()) {
392
503
  const beeJson = `BEEeditor${currentTab > 1 ? currentTab : ''}json`;
393
504
  const beeToken = `BEEeditor${currentTab > 1 ? currentTab : ''}token`;
394
- let beeJsonValue = _.get(nextProps, 'Templates.BEETemplate.versions.base.json-content', '');
505
+ let beeJsonValue = '';
395
506
  const selectedId = _.get(this.props, 'Email.templateDetails._id', '') || _.get(this.props, 'Templates.BEETemplate.versions.base.drag_drop_id', '');
396
- if (this.state.isEdit) {
397
- if (this.props.location.query.module === "library") {
398
- beeJsonValue = _.get(this.state, `formData[${currentTab - 1}][${langId}].json-content`, '');
399
- } else {
400
- beeJsonValue = _.get(nextProps, `Email.templateDetails.versions.base[${langId}].json-content`, '');
507
+
508
+ // Get beeJsonValue from template data - check multiple sources
509
+ // First check if it's already in formData (from setEditData)
510
+ beeJsonValue = _.get(this.state, `formData[${currentTab - 1}][${langId}].json-content`, '');
511
+
512
+ // Also check formData.base
513
+ if (!beeJsonValue) {
514
+ beeJsonValue = _.get(this.state, `formData.base[${langId}].json-content`, '');
515
+ }
516
+
517
+ // Always check templateDetails and BEETemplate regardless of mode
518
+ if (!beeJsonValue) {
519
+ // Check Email.templateDetails first
520
+ const templateDetails = nextProps.Email.templateDetails;
521
+ if (templateDetails && templateDetails.versions && templateDetails.versions.base) {
522
+ const baseVersion = templateDetails.versions.base;
523
+ // Try language-specific path first (e.g., base.en.json-content)
524
+ if (baseVersion[langId] && baseVersion[langId]['json-content']) {
525
+ beeJsonValue = baseVersion[langId]['json-content'];
526
+ } else if (baseVersion['json-content']) {
527
+ // Fallback to base-level json-content
528
+ beeJsonValue = baseVersion['json-content'];
529
+ }
530
+ }
531
+
532
+ // If still not found, check BEETemplate
533
+ if (!beeJsonValue) {
534
+ const beeTemplate = nextProps.Templates.BEETemplate;
535
+ if (beeTemplate && beeTemplate.versions && beeTemplate.versions.base) {
536
+ const beeBase = beeTemplate.versions.base;
537
+ if (beeBase[langId] && beeBase[langId]['json-content']) {
538
+ beeJsonValue = beeBase[langId]['json-content'];
539
+ } else if (beeBase['json-content']) {
540
+ beeJsonValue = beeBase['json-content'];
541
+ }
542
+ }
401
543
  }
402
544
  }
545
+ // Ensure we have a valid beeJsonValue - if it's a string, try to parse it to verify it's valid JSON
546
+ let finalBeeJsonValue = beeJsonValue;
547
+ if (beeJsonValue && typeof beeJsonValue === 'string') {
548
+ try {
549
+ // Try to parse to verify it's valid JSON
550
+ JSON.parse(beeJsonValue);
551
+ } catch (e) {
552
+ console.warn('[Email] componentWillReceiveProps - beeJsonValue is not valid JSON, using as-is:', e);
553
+ }
554
+ }
555
+
556
+ // Preserve existing formData values, especially template-subject
557
+ const existingFormData = formData[`${currentTab - 1}`][langId] || {};
403
558
  formData[`${currentTab - 1}`][langId] = {
404
- ...formData[`${currentTab - 1}`][langId],
559
+ ...existingFormData,
405
560
  is_drag_drop: true,
406
- [beeJson]: beeJsonValue,
561
+ [beeJson]: finalBeeJsonValue,
407
562
  [beeToken]: nextProps.Email.CmsSettings.tokenData,
408
563
  id: selectedId,
564
+ // Also store json-content for reference
565
+ 'json-content': finalBeeJsonValue,
409
566
  };
410
- _.forEach(temp.panes, (pane, index) => {
411
- const tempPane = pane;
412
- //
413
- if (parseInt(index, 10) === parseInt(langIndex, 10)) {
414
- //
415
- tempPane.sections[0].inputFields[0].cols[1].colStyle = {display: ""};
416
- tempPane.sections[0].inputFields[0].cols[0].colStyle = {display: "none"};
567
+
568
+ // Ensure template-subject is preserved at the top level
569
+ if (formData['template-subject'] === undefined || formData['template-subject'] === '') {
570
+ const subjectFromEditData = _.get(nextProps, 'Email.templateDetails.versions.base.subject', '');
571
+ if (subjectFromEditData) {
572
+ formData['template-subject'] = subjectFromEditData;
417
573
  }
418
- });
419
- this.setState({schema, isSchemaChanged: true, loadingStatus: this.state.loadingStatus + 1});
574
+ }
575
+
576
+ if (langIndex !== undefined && langIndex !== -1 && temp && temp.panes) {
577
+ _.forEach(temp.panes, (pane, index) => {
578
+ const tempPane = pane;
579
+ //
580
+ if (parseInt(index, 10) === parseInt(langIndex, 10)) {
581
+ //
582
+ if (tempPane.sections && tempPane.sections[0] && tempPane.sections[0].inputFields && tempPane.sections[0].inputFields[0] && tempPane.sections[0].inputFields[0].cols) {
583
+ tempPane.sections[0].inputFields[0].cols[1].colStyle = {display: ""};
584
+ tempPane.sections[0].inputFields[0].cols[0].colStyle = {display: "none"};
585
+ }
586
+ }
587
+ });
588
+ }
589
+ this.setState({schema, isSchemaChanged: true, loadingStatus: this.state.loadingStatus + 1, formData});
420
590
  } else {
421
591
  _.forEach(temp.panes, (pane, index) => {
422
592
  const tempPane = pane;
@@ -897,6 +1067,11 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
897
1067
  const isBEESupport = (this.props.location.query.isBEESupport !== "false") || false;
898
1068
  const formData = _.cloneDeep(this.state.formData);
899
1069
 
1070
+ // Set template name from editData if available
1071
+ if (editData.name) {
1072
+ formData['template-name'] = editData.name;
1073
+ }
1074
+
900
1075
  const schema = (this.props.location.query.type === 'embedded') ? this.removeStandAlone(this.state.schema) : _.cloneDeep(this.state.schema);
901
1076
  const containers = _.cloneDeep(schema.containers.slice());
902
1077
  let tabKey = "";
@@ -905,7 +1080,8 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
905
1080
  const type = this.props.location.query.type;
906
1081
 
907
1082
  formData['template-name'] = editData.name;
908
- formData['template-subject'] = _.get(editData, 'versions.base.subject');
1083
+ const subject = _.get(editData, 'versions.base.subject', '');
1084
+ formData['template-subject'] = subject;
909
1085
  formData.base = editData.versions.base;
910
1086
 
911
1087
  _.forEach(editData.versions.history, (data1, index) => {
@@ -986,8 +1162,13 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
986
1162
 
987
1163
 
988
1164
  this.setState({ formData, schema, tabKey, currentTab, tabCount: editData.versions.history.length, loadingStatus: this.state.loadingStatus + 1 }, () => {
989
- if (this.props.isFullMode) {
990
- this.props.showTemplateName({formData, onFormDataChange: this.onFormDataChange});
1165
+ if (this.props.isFullMode && this.props.showTemplateName) {
1166
+ // Ensure template name is set before showing it
1167
+ const updatedFormData = _.cloneDeep(formData);
1168
+ if (editData.name && !updatedFormData['template-name']) {
1169
+ updatedFormData['template-name'] = editData.name;
1170
+ }
1171
+ this.props.showTemplateName({formData: updatedFormData, onFormDataChange: this.onFormDataChange});
991
1172
  }
992
1173
  const isBEEAppEnable = this.checkBeeEditorAllowedForLibrary();
993
1174
  _.forEach((editData.versions.base.selectedLanguages), (language) => {
@@ -2663,6 +2844,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
2663
2844
  getTemplateDetailsInProgress,
2664
2845
  assetUploading,
2665
2846
  createTemplateInProgress,
2847
+ fetchingCmsSettings,
2666
2848
  } = this.props.Email;
2667
2849
  let isLoading =
2668
2850
  isLoadingMetaEntities ||
@@ -2671,7 +2853,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
2671
2853
  ) || (
2672
2854
  assetUploading !== undefined && assetUploading
2673
2855
  ) || (
2674
- Email && (fetchingCmsData || getTemplateDetailsInProgress)
2856
+ Email && (fetchingCmsData || getTemplateDetailsInProgress || fetchingCmsSettings)
2675
2857
  );
2676
2858
 
2677
2859
  if (!isLoading) {
@@ -2993,6 +3175,19 @@ function mapDispatchToProps(dispatch) {
2993
3175
  const withReducer = injectReducer({ key: 'email', reducer: v2EmailReducer });
2994
3176
  const withEmailSaga = injectSaga({ key: 'email', saga: v2EmailSagas });
2995
3177
 
3178
+ // Base Email component without saga registration (for use from EmailWrapper)
3179
+ // EmailWrapper already registers the saga, so we don't need to register it here
3180
+ export const EmailWithoutSaga = withCreatives({
3181
+ WrappedComponent: Email,
3182
+ mapStateToProps,
3183
+ mapDispatchToProps,
3184
+ userAuth: true,
3185
+ sagas: [], // No saga - EmailWrapper registers it
3186
+ reducers: [withReducer],
3187
+ });
3188
+
3189
+ // Email component with saga registration (for direct use from SlideBoxContent in Edit mode)
3190
+ // When Email is used directly (not as child of EmailWrapper), it needs to register the saga
2996
3191
  export default withCreatives({
2997
3192
  WrappedComponent: Email,
2998
3193
  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
  `;