@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.
- package/HOW_BEE_EDITOR_WORKS.md +375 -0
- package/constants/unified.js +1 -0
- package/package.json +1 -1
- package/services/api.js +5 -0
- package/utils/common.js +6 -1
- package/v2Components/CapTagList/index.js +2 -1
- package/v2Components/CapTagListWithInput/index.js +5 -1
- package/v2Components/CapTagListWithInput/messages.js +1 -1
- package/v2Components/ErrorInfoNote/style.scss +1 -1
- package/v2Components/HtmlEditor/HTMLEditor.js +86 -14
- package/v2Components/HtmlEditor/_htmlEditor.scss +4 -4
- package/v2Components/HtmlEditor/_index.lazy.scss +1 -1
- package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +107 -96
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +68 -92
- package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
- package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
- package/v2Containers/CreativesContainer/SlideBoxContent.js +85 -35
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +9 -3
- package/v2Containers/CreativesContainer/index.js +107 -35
- package/v2Containers/CreativesContainer/messages.js +4 -0
- package/v2Containers/Email/actions.js +7 -0
- package/v2Containers/Email/constants.js +5 -1
- package/v2Containers/Email/index.js +13 -0
- package/v2Containers/Email/messages.js +32 -0
- package/v2Containers/Email/reducer.js +12 -1
- package/v2Containers/Email/sagas.js +17 -0
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1005 -0
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +193 -7
- package/v2Containers/EmailWrapper/constants.js +2 -0
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +470 -71
- package/v2Containers/EmailWrapper/index.js +102 -23
- package/v2Containers/EmailWrapper/messages.js +61 -1
- package/v2Containers/EmailWrapper/tests/EmailHTMLEditor.test.js +177 -0
- package/v2Containers/EmailWrapper/tests/EmailHTMLEditorValidation.test.js +90 -0
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +49 -49
- package/v2Containers/TagList/index.js +2 -0
- 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
|
-
|
|
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({
|
|
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
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
&& (
|
|
1377
|
-
&& currentStep === 'modeSelection')) {
|
|
1378
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
)}
|
|
@@ -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
|
}
|