@capillarytech/creatives-library 8.0.213 → 8.0.214-beta.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 (37) hide show
  1. package/HOW_BEE_EDITOR_WORKS.md +375 -0
  2. package/constants/unified.js +1 -0
  3. package/package.json +1 -1
  4. package/services/api.js +5 -0
  5. package/utils/common.js +6 -1
  6. package/v2Components/CapTagList/index.js +2 -1
  7. package/v2Components/CapTagListWithInput/index.js +5 -1
  8. package/v2Components/CapTagListWithInput/messages.js +1 -1
  9. package/v2Components/ErrorInfoNote/style.scss +1 -1
  10. package/v2Components/HtmlEditor/HTMLEditor.js +86 -14
  11. package/v2Components/HtmlEditor/_htmlEditor.scss +4 -4
  12. package/v2Components/HtmlEditor/_index.lazy.scss +1 -1
  13. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +107 -96
  14. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +68 -92
  15. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  16. package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
  17. package/v2Containers/CreativesContainer/SlideBoxContent.js +85 -35
  18. package/v2Containers/CreativesContainer/SlideBoxFooter.js +9 -3
  19. package/v2Containers/CreativesContainer/index.js +107 -35
  20. package/v2Containers/CreativesContainer/messages.js +4 -0
  21. package/v2Containers/Email/actions.js +7 -0
  22. package/v2Containers/Email/constants.js +5 -1
  23. package/v2Containers/Email/index.js +13 -0
  24. package/v2Containers/Email/messages.js +32 -0
  25. package/v2Containers/Email/reducer.js +12 -1
  26. package/v2Containers/Email/sagas.js +17 -0
  27. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1005 -0
  28. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +193 -7
  29. package/v2Containers/EmailWrapper/constants.js +2 -0
  30. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +470 -71
  31. package/v2Containers/EmailWrapper/index.js +102 -23
  32. package/v2Containers/EmailWrapper/messages.js +61 -1
  33. package/v2Containers/EmailWrapper/tests/EmailHTMLEditor.test.js +177 -0
  34. package/v2Containers/EmailWrapper/tests/EmailHTMLEditorValidation.test.js +90 -0
  35. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +49 -49
  36. package/v2Containers/TagList/index.js +2 -0
  37. package/v2Containers/Templates/index.js +5 -0
@@ -43,7 +43,7 @@ import {
43
43
  IMAGE as LINE_IMAGE, IMAGE_MAP, IMAGE_CAROUSEL, VIDEO as LINE_VIDEO, TEMPLATE, STICKER,
44
44
  } from '../Line/Container/constants';
45
45
  import { IMAGE, VIDEO } from '../Facebook/Advertisement/constant';
46
- import {RCS_STATUSES} from '../Rcs/constants';
46
+ import { RCS_STATUSES } from '../Rcs/constants';
47
47
  import { CREATIVE } from '../Facebook/constants';
48
48
  import { LOYALTY } from '../App/constants';
49
49
  import {
@@ -107,6 +107,9 @@ export class Creatives extends React.Component {
107
107
  isTestAndPreviewMode: false, // Add flag to track Test & Preview mode
108
108
  // Performance optimization: Local template name for immediate UI feedback
109
109
  localTemplateName: '',
110
+ // Track selected email create mode for new flow (HTML Editor vs Drag & Drop)
111
+ selectedEmailCreateMode: null,
112
+ // isTemplateNameEmpty: true, // Track template name state for new flow
110
113
  };
111
114
  this.liquidFlow = Boolean(commonUtil.hasLiquidSupportFeature());
112
115
  this.creativesTemplateSteps = {
@@ -136,7 +139,7 @@ export class Creatives extends React.Component {
136
139
  if (!this.props?.isFullMode) {
137
140
  this.props?.templateActions.getCdnTransformationConfig();
138
141
  }
139
-
142
+
140
143
  // Store loyalty tag props if loyaltyTagFetchingDependencies is provided
141
144
  const { loyaltyTagFetchingDependencies } = this.props;
142
145
  if (loyaltyTagFetchingDependencies) {
@@ -162,9 +165,9 @@ export class Creatives extends React.Component {
162
165
  const isEmptyTemplateName = !value.trim();
163
166
 
164
167
  // 1. IMMEDIATE: Update local state for instant UI feedback
165
- this.setState({
168
+ this.setState({
166
169
  isTemplateNameEmpty: isEmptyTemplateName,
167
- localTemplateName: value
170
+ localTemplateName: value
168
171
  });
169
172
 
170
173
  // 2. DEBOUNCED: Only debounce the expensive onFormDataChange call
@@ -243,8 +246,19 @@ export class Creatives extends React.Component {
243
246
  onCreateNextStep = () => {
244
247
  this.setState((prevState) => {
245
248
  let templateStep = prevState.templateStep + 1;
246
- const { emailCreateMode, currentChannel } = prevState;
247
- if ((currentChannel.toUpperCase() === constants.EMAIL && emailCreateMode === "upload") || [constants.MOBILE_PUSH, constants.WECHAT, constants.INAPP].includes(currentChannel.toUpperCase())) {
249
+ const { emailCreateMode, currentChannel, selectedEmailCreateMode } = prevState;
250
+
251
+ // Check if we should skip template selection for HTML Editor
252
+ const supportCKEditor = commonUtil.hasSupportCKEditor();
253
+ const shouldSkipTemplateSelection = !supportCKEditor
254
+ && selectedEmailCreateMode === 'html_editor'
255
+ && currentChannel.toUpperCase() === constants.EMAIL
256
+ && prevState.templateStep === 1; // Only skip if we're at modeSelection step
257
+
258
+ if (shouldSkipTemplateSelection) {
259
+ // Skip template selection (step 2), go directly to createTemplateContent (step 3)
260
+ templateStep = prevState.templateStep + 2;
261
+ } else if ((currentChannel.toUpperCase() === constants.EMAIL && emailCreateMode === "upload") || [constants.MOBILE_PUSH, constants.WECHAT, constants.INAPP].includes(currentChannel.toUpperCase())) {
248
262
  templateStep = prevState.templateStep + 2;
249
263
  }
250
264
  return {
@@ -253,8 +267,11 @@ export class Creatives extends React.Component {
253
267
  });
254
268
  }
255
269
 
256
- onEmailModeChange = (mode) => {
257
- this.setState({ emailCreateMode: mode });
270
+ onEmailModeChange = (mode, selectedMode = null) => {
271
+ this.setState({
272
+ emailCreateMode: mode,
273
+ selectedEmailCreateMode: selectedMode || mode, // Store the selected mode for new flow
274
+ });
258
275
  }
259
276
 
260
277
  onInAppModeChange = (mode) => {
@@ -304,7 +321,7 @@ export class Creatives extends React.Component {
304
321
  }
305
322
  return buttonObj;
306
323
  });
307
- const {url, previewUrl} = media || {};
324
+ const { url, previewUrl } = media || {};
308
325
  return {
309
326
  bodyText: bodyTemplate,
310
327
  varMap: cardVarMapped,
@@ -673,7 +690,7 @@ export class Creatives extends React.Component {
673
690
  } = templateData || {};
674
691
  const cardContent = (rcsContent.cardContent && rcsContent.cardContent[0]) || {};
675
692
  const Status = RCS_STATUSES.approved || '';
676
-
693
+
677
694
  creativesTemplateData = {
678
695
  type: channel,
679
696
  edit: true,
@@ -758,7 +775,7 @@ export class Creatives extends React.Component {
758
775
  });
759
776
 
760
777
  getMobilePushCarouselData = (expandableDetails = []) => {
761
- const newExpandableDetails = {...expandableDetails};
778
+ const newExpandableDetails = { ...expandableDetails };
762
779
  newExpandableDetails.style = expandableDetails.style || MANUAL_CAROUSEL;
763
780
  newExpandableDetails.message = expandableDetails.message || '';
764
781
  newExpandableDetails.ctas = expandableDetails.ctas || [];
@@ -853,7 +870,7 @@ export class Creatives extends React.Component {
853
870
  androidContent.custom = custom;
854
871
  }
855
872
  if (channel === constants.MOBILE_PUSH && androidContent?.expandableDetails?.carouselData?.length) {
856
- androidContent.expandableDetails = this.getMobilePushCarouselData({...androidContent?.expandableDetails});
873
+ androidContent.expandableDetails = this.getMobilePushCarouselData({ ...androidContent?.expandableDetails });
857
874
  }
858
875
  templateData.androidContent = androidContent;
859
876
  templateData.androidContent.type = androidContent?.type || get(channelTemplate, 'definition.mode', '')?.toUpperCase() || constants.TEXT;
@@ -875,7 +892,7 @@ export class Creatives extends React.Component {
875
892
  iosContent.custom = custom;
876
893
  }
877
894
  if (channel === constants.MOBILE_PUSH && iosContent?.expandableDetails?.carouselData?.length) {
878
- iosContent.expandableDetails = this.getMobilePushCarouselData({...iosContent?.expandableDetails});
895
+ iosContent.expandableDetails = this.getMobilePushCarouselData({ ...iosContent?.expandableDetails });
879
896
  }
880
897
  templateData.iosContent = iosContent;
881
898
  templateData.iosContent.type = iosContent?.type || get(channelTemplate, 'definition.mode', '')?.toUpperCase() || 'TEXT';
@@ -1092,7 +1109,7 @@ export class Creatives extends React.Component {
1092
1109
  contentType = "",
1093
1110
  cardType = "",
1094
1111
  cardSettings = {},
1095
- } = get(versions, 'base.content.RCS.rcsContent',{});
1112
+ } = get(versions, 'base.content.RCS.rcsContent', {});
1096
1113
  const rcsContent = {
1097
1114
  contentType,
1098
1115
  cardType,
@@ -1230,7 +1247,7 @@ export class Creatives extends React.Component {
1230
1247
  processCentralCommsMetaId = (channel, creativesData) => {
1231
1248
  // Create the payload for the centralcommnsmetaId API call
1232
1249
  const { isLoyaltyModule = false, loyaltyMetaData = {} } = this.props;
1233
- const { actionName, setMetaData = () => {} } = loyaltyMetaData;
1250
+ const { actionName, setMetaData = () => { } } = loyaltyMetaData;
1234
1251
 
1235
1252
  // const isTemplateModified = getTemplateDiffState(
1236
1253
  // channel,
@@ -1333,6 +1350,13 @@ export class Creatives extends React.Component {
1333
1350
  showFooter = isFullMode && slidBoxContent === "preview";
1334
1351
  const isMobilepush = channel === constants.MOBILE_PUSH;
1335
1352
 
1353
+ const supportCKEditor = commonUtil.hasSupportCKEditor();
1354
+ if (!supportCKEditor && channel === constants.EMAIL && currentStep === 'modeSelection' && slidBoxContent === 'createTemplate') {
1355
+ return true;
1356
+ }
1357
+ if (!supportCKEditor && channel === constants.EMAIL && currentStep === 'createTemplateContent' && slidBoxContent === 'createTemplate') {
1358
+ showFooter = true;
1359
+ }
1336
1360
 
1337
1361
  if (!isFullMode) {
1338
1362
  const isEmailCreate = slidBoxContent === 'createTemplate' && channel === constants.EMAIL && currentStep !== 'createTemplateContent';
@@ -1369,13 +1393,18 @@ export class Creatives extends React.Component {
1369
1393
  }
1370
1394
 
1371
1395
  if (showFooter) {
1372
- if (slidBoxContent === "createTemplate" && ((channel === constants.EMAIL && currentStep === 'createTemplateContent')
1373
- || ([constants.SMS, constants.WECHAT].includes(channel) && currentStep === 'modeSelection'))) {
1374
- showFooter = showFooter && !this.state.isLoadingContent;
1375
- } else if (slidBoxContent === "editTemplate"
1376
- && ([constants.SMS, constants.WECHAT, constants.EMAIL].includes(channel)
1377
- && currentStep === 'modeSelection')) {
1378
- showFooter = showFooter && !this.state.isLoadingContent;
1396
+ // For new HTML editor flow, don't check isLoadingContent
1397
+ const isNewHTMLEditorFlow = !supportCKEditor && channel === constants.EMAIL && currentStep === 'createTemplateContent' && slidBoxContent === 'createTemplate';
1398
+
1399
+ if (!isNewHTMLEditorFlow) {
1400
+ if (slidBoxContent === "createTemplate" && ((channel === constants.EMAIL && currentStep === 'createTemplateContent')
1401
+ || ([constants.SMS, constants.WECHAT].includes(channel) && currentStep === 'modeSelection'))) {
1402
+ showFooter = showFooter && !this.state.isLoadingContent;
1403
+ } else if (slidBoxContent === "editTemplate"
1404
+ && ([constants.SMS, constants.WECHAT, constants.EMAIL].includes(channel)
1405
+ && currentStep === 'modeSelection')) {
1406
+ showFooter = showFooter && !this.state.isLoadingContent;
1407
+ }
1379
1408
  }
1380
1409
  }
1381
1410
 
@@ -1398,10 +1427,17 @@ export class Creatives extends React.Component {
1398
1427
  const channelName = !isFullMode && templateData ? templateData.type : currentChannel;
1399
1428
  const channel = channelName?.toUpperCase();
1400
1429
 
1401
-
1430
+ // Check supportCKEditor flag for new HTML editor flow
1431
+ const supportCKEditor = commonUtil.hasSupportCKEditor();
1402
1432
  if (channel === constants.EMAIL || channel === constants.SMS) {
1403
1433
  const isEmailCreate = slidBoxContent === 'createTemplate' && channel === constants.EMAIL && currentStep !== 'createTemplateContent';
1404
- showDone = (slidBoxContent === 'editTemplate' || slidBoxContent === 'createTemplate') && !isEmailCreate;
1434
+
1435
+ // For new HTML editor flow (when supportCKEditor is false), show Done footer when in createTemplateContent step
1436
+ if (!supportCKEditor && channel === constants.EMAIL && slidBoxContent === 'createTemplate' && currentStep === 'createTemplateContent') {
1437
+ showDone = true;
1438
+ } else {
1439
+ showDone = (slidBoxContent === 'editTemplate' || slidBoxContent === 'createTemplate') && !isEmailCreate;
1440
+ }
1405
1441
  } else if ([constants.WECHAT, constants.MOBILE_PUSH].includes(channel)) {
1406
1442
  showDone = currentStep === "createTemplateContent" || slidBoxContent === "editTemplate";
1407
1443
 
@@ -1411,7 +1447,6 @@ export class Creatives extends React.Component {
1411
1447
  }
1412
1448
  }
1413
1449
 
1414
-
1415
1450
  return showDone;
1416
1451
  }
1417
1452
 
@@ -1431,18 +1466,18 @@ export class Creatives extends React.Component {
1431
1466
  templateNameComponentInput = ({ formData, onFormDataChange, name }) => {
1432
1467
  // Use local state for immediate UI feedback, fallback to prop value
1433
1468
  const displayValue = this.state.localTemplateName !== '' ? this.state.localTemplateName : name;
1434
-
1469
+
1435
1470
  return (
1436
1471
  <CapInput
1437
1472
  value={displayValue}
1438
1473
  suffix={<span />}
1439
- onBlur={() => {
1440
- this.setState({
1474
+ onBlur={() => {
1475
+ this.setState({
1441
1476
  isEditName: false,
1442
1477
  localTemplateName: '' // Clear local state on blur
1443
- }, () => {
1444
- this.showTemplateName({ formData, onFormDataChange });
1445
- });
1478
+ }, () => {
1479
+ this.showTemplateName({ formData, onFormDataChange });
1480
+ });
1446
1481
  }}
1447
1482
  onChange={(ev) => {
1448
1483
  const { value } = ev.currentTarget;
@@ -1454,10 +1489,16 @@ export class Creatives extends React.Component {
1454
1489
  }
1455
1490
 
1456
1491
  showTemplateName = ({ formData, onFormDataChange }) => { //gets called from email/index after template data is fetched
1457
- const { slidBoxContent, currentChannel, isEditName } = this.state;
1492
+ const { slidBoxContent, currentChannel, isEditName, templateStep } = this.state;
1458
1493
  const channel = currentChannel.toUpperCase();
1459
1494
  if ([constants.EMAIL, constants.MOBILE_PUSH, constants.INAPP].includes(channel) && (slidBoxContent === 'editTemplate' || slidBoxContent === 'createTemplate')) {
1460
1495
  const name = get(formData, 'template-name');
1496
+
1497
+ const isModeSelectionStep = templateStep === 'modeSelection' || this.creativesTemplateSteps[templateStep] === 'modeSelection';
1498
+ const isCreateMode = slidBoxContent === 'createTemplate';
1499
+ if (isCreateMode && isModeSelectionStep) {
1500
+ return;
1501
+ }
1461
1502
  if (channel === constants.EMAIL && !name && slidBoxContent === 'createTemplate') {
1462
1503
  this.setState({ isTemplateNameEmpty: true });
1463
1504
  }
@@ -1465,7 +1506,7 @@ export class Creatives extends React.Component {
1465
1506
  if (name && !isEditName) {
1466
1507
  this.setState({ showTemplateNameComponentEdit: false });
1467
1508
  } else if (isEditName) {
1468
- this.setState({
1509
+ this.setState({
1469
1510
  showTemplateNameComponentEdit: true,
1470
1511
  localTemplateName: name || '' // Initialize local state with current value
1471
1512
  });
@@ -1488,8 +1529,17 @@ export class Creatives extends React.Component {
1488
1529
  let isShowContinueFooter = false;
1489
1530
  const currentStep = this.creativesTemplateSteps[templateStep];
1490
1531
  const channel = currentChannel.toUpperCase();
1532
+ // Check if supportCKEditor is false (new flow)
1533
+ const supportCKEditor = commonUtil.hasSupportCKEditor(); // Default to legacy flow
1491
1534
  if (channel === constants.EMAIL || channel === constants.SMS) {
1492
- if ((emailCreateMode === "upload" && !isEmpty(this.props.EmailLayout)) || emailCreateMode === "editor") {
1535
+ // New flow: Show Continue button when supportCKEditor is false and in modeSelection
1536
+ // Always show it (even if disabled) - visibility is separate from enabled state
1537
+ if (!supportCKEditor && currentStep === 'modeSelection' && slidBoxContent === 'createTemplate') {
1538
+ return true; // Return early to ensure visibility
1539
+ }
1540
+
1541
+ // Legacy flow: Original logic (only when supportCKEditor is true)
1542
+ if (supportCKEditor && ((emailCreateMode === "upload" && !isEmpty(this.props.EmailLayout)) || emailCreateMode === "editor")) {
1493
1543
  let isEmailCreate = slidBoxContent === 'createTemplate';
1494
1544
  isEmailCreate = currentChannel.toUpperCase() === constants.EMAIL && ((emailCreateMode === "upload" && currentStep !== 'createTemplateContent') || (emailCreateMode === "editor" && currentStep !== 'createTemplateContent' && currentStep !== "templateSelection"));
1495
1545
  isShowContinueFooter = isEmailCreate && emailCreateMode;
@@ -1525,6 +1575,22 @@ export class Creatives extends React.Component {
1525
1575
  return true;
1526
1576
  }
1527
1577
 
1578
+ // Check if Continue button should be disabled (for new flow only)
1579
+ isContinueButtonDisabled = () => {
1580
+ const { currentChannel, emailCreateMode, templateNameExists } = this.state;
1581
+ const { isFullMode } = this.props;
1582
+ const supportCKEditor = commonUtil.hasSupportCKEditor();
1583
+ if (supportCKEditor) {
1584
+ return false;
1585
+ }
1586
+ if (currentChannel.toUpperCase() === constants.EMAIL) {
1587
+ const isTemplateNameValid = templateNameExists && isFullMode;
1588
+ const isEditorSelected = !!emailCreateMode && emailCreateMode !== 'upload';
1589
+ return !(isTemplateNameValid && isEditorSelected);
1590
+ }
1591
+ return true;
1592
+ }
1593
+
1528
1594
  render() {
1529
1595
  const {
1530
1596
  slidBoxContent,
@@ -1567,6 +1633,10 @@ export class Creatives extends React.Component {
1567
1633
  isLoyaltyModule,
1568
1634
  loyaltyMetaData = {},
1569
1635
  } = this.props;
1636
+ // Compute Continue button label
1637
+ const supportCKEditor = commonUtil.hasSupportCKEditor();
1638
+ const continueButtonLabel = supportCKEditor ? messages.continue : messages.next;
1639
+
1570
1640
  const mapTemplateCreate = slidBoxContent === "createTemplate"
1571
1641
  && weChatTemplateType === MAP_TEMPLATE
1572
1642
  && templateStep !== "modeSelection";
@@ -1697,6 +1767,8 @@ export class Creatives extends React.Component {
1697
1767
  errorMessages={liquidErrorMessage}
1698
1768
  currentTab={activeFormBuilderTab}
1699
1769
  onTestAndPreview={this.handleTestAndPreview}
1770
+ isContinueButtonDisabled={this.isContinueButtonDisabled()}
1771
+ continueButtonLabel={continueButtonLabel}
1700
1772
  showTestAndPreviewButton={(() => {
1701
1773
  const showButton = currentChannel.toUpperCase() === constants.EMAIL && (slidBoxContent === 'editTemplate' || slidBoxContent === 'createTemplate');
1702
1774
  return showButton;
@@ -1707,7 +1779,7 @@ export class Creatives extends React.Component {
1707
1779
  {(() => {
1708
1780
  const errorsToShow = get(liquidErrorMessage, constants.LIQUID_ERROR_MSG, []);
1709
1781
  const standardErrorsToShow = get(liquidErrorMessage, constants.STANDARD_ERROR_MSG, []);
1710
- return <ErrorInfoNote currentTab={activeFormBuilderTab?.toUpperCase()} errorMessages={{LIQUID_ERROR_MSG: errorsToShow, STANDARD_ERROR_MSG: standardErrorsToShow}} />;
1782
+ return <ErrorInfoNote currentTab={activeFormBuilderTab?.toUpperCase()} errorMessages={{ LIQUID_ERROR_MSG: errorsToShow, STANDARD_ERROR_MSG: standardErrorsToShow }} />;
1711
1783
  })()}
1712
1784
  </CapRow>
1713
1785
  )}
@@ -366,4 +366,8 @@ export default defineMessages({
366
366
  id: `${scope}.testAndPreview`,
367
367
  defaultMessage: `Preview and Test`,
368
368
  },
369
+ "next": {
370
+ id: `${scope}.next`,
371
+ defaultMessage: `Next`,
372
+ },
369
373
  });
@@ -66,6 +66,13 @@ export function clearCRUDResponse() {
66
66
  };
67
67
  }
68
68
 
69
+ export function getCmsAccounts(cmsType) {
70
+ return {
71
+ type: types.GET_CMS_ACCOUNTS_REQUEST,
72
+ cmsType,
73
+ };
74
+ }
75
+
69
76
  export function clearStoreValues() {
70
77
  return {
71
78
  type: types.CLEAR_ALL_VALUES,
@@ -36,4 +36,8 @@ export const DUPLICATE_TEMPLATE_REQUEST = 'app/v2Containers/Email/DUPLICATE_TEMP
36
36
  export const DUPLICATE_TEMPLATE_SUCCESS = 'app/v2Containers/Email/DUPLICATE_TEMPLATE_SUCCESS';
37
37
  export const DUPLICATE_TEMPLATE_FAILURE = 'app/v2Containers/Email/DUPLICATE_TEMPLATE_FAILURE';
38
38
 
39
- export const TRANSFORM_EMAIL_TEMPLATE_REQUEST = 'app/v2Containers/Email/TRANSFORM_EMAIL_TEMPLATE_REQUEST';
39
+ export const TRANSFORM_EMAIL_TEMPLATE_REQUEST = 'app/v2Containers/Email/TRANSFORM_EMAIL_TEMPLATE_REQUEST';
40
+
41
+ export const GET_CMS_ACCOUNTS_REQUEST = 'app/v2Containers/Email/GET_CMS_ACCOUNTS_REQUEST';
42
+ export const GET_CMS_ACCOUNTS_SUCCESS = 'app/v2Containers/Email/GET_CMS_ACCOUNTS_SUCCESS';
43
+ export const GET_CMS_ACCOUNTS_FAILURE = 'app/v2Containers/Email/GET_CMS_ACCOUNTS_FAILURE';
@@ -2998,6 +2998,19 @@ function mapDispatchToProps(dispatch) {
2998
2998
  const withReducer = injectReducer({ key: 'email', reducer: v2EmailReducer });
2999
2999
  const withEmailSaga = injectSaga({ key: 'email', saga: v2EmailSagas });
3000
3000
 
3001
+ // Base Email component without saga registration (for use from EmailWrapper)
3002
+ // EmailWrapper already registers the saga, so we don't need to register it here
3003
+ export const EmailWithoutSaga = withCreatives({
3004
+ WrappedComponent: Email,
3005
+ mapStateToProps,
3006
+ mapDispatchToProps,
3007
+ userAuth: true,
3008
+ sagas: [], // No saga - EmailWrapper registers it
3009
+ reducers: [withReducer],
3010
+ });
3011
+
3012
+ // Email component with saga registration (for direct use from SlideBoxContent in Edit mode)
3013
+ // When Email is used directly (not as child of EmailWrapper), it needs to register the saga
3001
3014
  export default withCreatives({
3002
3015
  WrappedComponent: Email,
3003
3016
  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:
@@ -87,6 +87,17 @@ export function* getCmsData({cmsType, projectId, langId}) {
87
87
  }
88
88
  }
89
89
 
90
+ export function* getCmsAccounts({cmsType}) {
91
+ try {
92
+ const result = yield call(Api.getCmsAccounts, cmsType);
93
+ const { cmsAccounts } = result.data?.response || {};
94
+ const isBeeEnabled = cmsAccounts?.type === cmsType;
95
+ yield put({ type: types.GET_CMS_ACCOUNTS_SUCCESS, isBeeEnabled });
96
+ } catch (error) {
97
+ yield put({ type: types.GET_CMS_ACCOUNTS_FAILURE, error });
98
+ }
99
+ }
100
+
90
101
  export function* uploadAsset(file, assetType, fileParams) {
91
102
  try {
92
103
  const result = yield call(Api.uploadFile, file, assetType, fileParams);
@@ -123,6 +134,10 @@ function* watchGetCmsData() {
123
134
  yield takeEvery(types.GET_CMS_EDITOR_DATA_REQUEST, getCmsData);
124
135
  }
125
136
 
137
+ function* watchGetCmsAccounts() {
138
+ yield takeEvery(types.GET_CMS_ACCOUNTS_REQUEST, getCmsAccounts);
139
+ }
140
+
126
141
  function* watchUploadAsset() {
127
142
  yield takeLatest(types.UPLOAD_ASSET_REQUEST, uploadAsset);
128
143
  }
@@ -139,6 +154,7 @@ export default [
139
154
  watchGetAllAssets,
140
155
  watchGetCmsSetting,
141
156
  watchGetCmsData,
157
+ watchGetCmsAccounts,
142
158
  watchUploadAsset,
143
159
  watchDuplicateTemplate,
144
160
  ];
@@ -151,6 +167,7 @@ export function* v2EmailSagas() {
151
167
  watchGetAllAssets(),
152
168
  watchGetCmsSetting(),
153
169
  watchGetCmsData(),
170
+ watchGetCmsAccounts(),
154
171
  watchUploadAsset(),
155
172
  ]);
156
173
  }