@capillarytech/creatives-library 8.0.285 → 8.0.287-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.
- package/constants/unified.js +0 -1
- package/initialState.js +0 -2
- package/package.json +1 -1
- package/utils/common.js +5 -8
- package/utils/commonUtils.js +2 -83
- package/utils/tagValidations.js +84 -222
- package/utils/tests/commonUtil.test.js +147 -118
- package/utils/tests/tagValidations.test.js +280 -358
- package/v2Components/ErrorInfoNote/index.js +2 -5
- package/v2Components/FormBuilder/index.js +64 -158
- package/v2Components/FormBuilder/messages.js +0 -8
- package/v2Components/HtmlEditor/HTMLEditor.js +0 -5
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +0 -1
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +0 -15
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +1 -2
- package/v2Containers/Cap/mockData.js +0 -14
- package/v2Containers/Cap/reducer.js +3 -55
- package/v2Containers/Cap/tests/reducer.test.js +0 -102
- package/v2Containers/CreativesContainer/index.js +0 -1
- package/v2Containers/Email/index.js +1 -5
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +10 -62
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +12 -115
- package/v2Containers/FTP/index.js +2 -51
- package/v2Containers/FTP/messages.js +0 -4
- package/v2Containers/InApp/index.js +1 -96
- package/v2Containers/InApp/tests/index.test.js +17 -6
- package/v2Containers/InappAdvance/index.js +2 -103
- package/v2Containers/Line/Container/Text/index.js +0 -1
- package/v2Containers/MobilePushNew/index.js +2 -33
- package/v2Containers/Rcs/index.js +12 -37
- package/v2Containers/SmsTrai/Create/index.scss +1 -1
- package/v2Containers/SmsTrai/Edit/index.js +6 -47
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +6 -6
- package/v2Containers/Viber/index.js +0 -1
- package/v2Containers/Viber/index.scss +1 -1
- package/v2Containers/WebPush/Create/hooks/useTagManagement.js +1 -3
- package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -7
- package/v2Containers/WebPush/Create/index.js +2 -2
- package/v2Containers/WebPush/Create/utils/validation.js +18 -9
- package/v2Containers/WebPush/Create/utils/validation.test.js +0 -24
- package/v2Containers/Whatsapp/index.js +9 -17
- package/v2Containers/Zalo/index.js +3 -11
|
@@ -186,7 +186,6 @@ export const ErrorInfoNote = (props) => {
|
|
|
186
186
|
errorMessages,
|
|
187
187
|
onErrorClick,
|
|
188
188
|
onClose,
|
|
189
|
-
isLiquidEnabled = true,
|
|
190
189
|
intl,
|
|
191
190
|
useLegacyDisplay = false, // Use simple list display instead of tabs (for BEE Editor)
|
|
192
191
|
} = props;
|
|
@@ -230,7 +229,7 @@ export const ErrorInfoNote = (props) => {
|
|
|
230
229
|
const standardErrors = Array.isArray(rawStandardErrors) ? rawStandardErrors : [];
|
|
231
230
|
const liquidErrors = Array.isArray(rawLiquidErrors) ? rawLiquidErrors : [];
|
|
232
231
|
const hasStandardErrors = standardErrors.length > 0;
|
|
233
|
-
const hasLiquidErrors = liquidErrors.length > 0
|
|
232
|
+
const hasLiquidErrors = liquidErrors.length > 0;
|
|
234
233
|
|
|
235
234
|
if (!hasStandardErrors && !hasLiquidErrors) {
|
|
236
235
|
return null;
|
|
@@ -357,7 +356,7 @@ export const ErrorInfoNote = (props) => {
|
|
|
357
356
|
className="error-info-note__tabs"
|
|
358
357
|
/>
|
|
359
358
|
<CapRow className="error-info-note__actions">
|
|
360
|
-
{hasLiquidErrors &&
|
|
359
|
+
{hasLiquidErrors && (
|
|
361
360
|
<CapButton
|
|
362
361
|
type="flat"
|
|
363
362
|
className="error-info-note__liquid-doc"
|
|
@@ -452,7 +451,6 @@ ErrorInfoNote.defaultProps = {
|
|
|
452
451
|
},
|
|
453
452
|
onErrorClick: null,
|
|
454
453
|
onClose: null,
|
|
455
|
-
isLiquidEnabled: true,
|
|
456
454
|
intl: null,
|
|
457
455
|
useLegacyDisplay: false, // Use simple list display for BEE Editor
|
|
458
456
|
};
|
|
@@ -479,7 +477,6 @@ ErrorInfoNote.propTypes = {
|
|
|
479
477
|
}),
|
|
480
478
|
onErrorClick: PropTypes.func,
|
|
481
479
|
onClose: PropTypes.func,
|
|
482
|
-
isLiquidEnabled: PropTypes.bool,
|
|
483
480
|
intl: PropTypes.object,
|
|
484
481
|
useLegacyDisplay: PropTypes.bool, // Use simple list display for BEE Editor
|
|
485
482
|
};
|
|
@@ -50,7 +50,7 @@ import { makeSelectMetaEntities, selectCurrentOrgDetails, selectLiquidStateDetai
|
|
|
50
50
|
import * as actions from "../../v2Containers/Cap/actions";
|
|
51
51
|
import './_formBuilder.scss';
|
|
52
52
|
import {updateCharCount, checkUnicode} from "../../utils/smsCharCountV2";
|
|
53
|
-
import {
|
|
53
|
+
import { preprocessHtml, validateTagsCore, hasUnsubscribeTag } from '../../utils/tagValidations';
|
|
54
54
|
import { containsBase64Images } from '../../utils/content';
|
|
55
55
|
import { SMS, MOBILE_PUSH, LINE, ENABLE_AI_SUGGESTIONS,AI_CONTENT_BOT_DISABLED, EMAIL, LIQUID_SUPPORTED_CHANNELS, INAPP } from '../../v2Containers/CreativesContainer/constants';
|
|
56
56
|
import globalMessages from '../../v2Containers/Cap/messages';
|
|
@@ -60,7 +60,7 @@ import { GET_TRANSLATION_MAPPED } from '../../constants/unified';
|
|
|
60
60
|
import moment from 'moment';
|
|
61
61
|
import { CUSTOMER_BARCODE_TAG , COPY_OF, ENTRY_TRIGGER_TAG_REGEX, SKIP_TAGS_REGEX_GROUPS} from '../../constants/unified';
|
|
62
62
|
import { REQUEST } from '../../v2Containers/Cap/constants'
|
|
63
|
-
import {
|
|
63
|
+
import { isEmailUnsubscribeTagOptional } from '../../utils/common';
|
|
64
64
|
import { isUrl } from '../../v2Containers/Line/Container/Wrapper/utils';
|
|
65
65
|
import { bindActionCreators } from 'redux';
|
|
66
66
|
import { getChannelData, validateLiquidTemplateContent, validateMobilePushContent } from '../../utils/commonUtils';
|
|
@@ -72,10 +72,8 @@ const {CapRadioGroup} = CapRadio;
|
|
|
72
72
|
|
|
73
73
|
const tagsTypes = {
|
|
74
74
|
MISSING_TAGS: 'missingTags',
|
|
75
|
-
UNSUPPORTED_TAGS: 'unsupportedTags',
|
|
76
75
|
};
|
|
77
76
|
const errorMessageForTags = {
|
|
78
|
-
UNSUPPORTED_TAG_ERROR: 'unsupportedTagsError',
|
|
79
77
|
MISSING_TAG_ERROR: 'missingTagsError',
|
|
80
78
|
GENERIC_VALIDATION_ERROR: 'genericValidationError',
|
|
81
79
|
TAG_BRACKET_COUNT_MISMATCH_ERROR: 'tagBracketCountMismatchError'
|
|
@@ -138,7 +136,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
138
136
|
this.handleSetRadioValue = this.handleSetRadioValue.bind(this);
|
|
139
137
|
this.formElements = [];
|
|
140
138
|
// Check if the liquid flow feature is supported and the channel is in the supported list.
|
|
141
|
-
this.
|
|
139
|
+
this.isLiquidFlowSupportedByChannel = this.isLiquidFlowSupportedByChannel.bind(this);
|
|
142
140
|
this.onSubmitWrapper = this.onSubmitWrapper.bind(this);
|
|
143
141
|
|
|
144
142
|
// Performance optimization: Debounced functions for high-frequency updates
|
|
@@ -330,8 +328,8 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
330
328
|
return updatedFormData;
|
|
331
329
|
}
|
|
332
330
|
|
|
333
|
-
|
|
334
|
-
return Boolean(LIQUID_SUPPORTED_CHANNELS.includes(this.props?.schema?.channel?.toUpperCase())
|
|
331
|
+
isLiquidFlowSupportedByChannel = () => {
|
|
332
|
+
return Boolean(LIQUID_SUPPORTED_CHANNELS.includes(this.props?.schema?.channel?.toUpperCase()));
|
|
335
333
|
}
|
|
336
334
|
|
|
337
335
|
componentWillMount() {
|
|
@@ -726,17 +724,19 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
726
724
|
|
|
727
725
|
let tagValidationResponse = false;
|
|
728
726
|
if (content) {
|
|
729
|
-
tagValidationResponse = this.validateTags(content, tags,
|
|
727
|
+
tagValidationResponse = this.validateTags(content, tags, false, this.props?.isFullMode);
|
|
730
728
|
}
|
|
731
|
-
|
|
732
|
-
|
|
729
|
+
|
|
730
|
+
const tagResult = tagValidationResponse && typeof tagValidationResponse === 'object'
|
|
731
|
+
? tagValidationResponse
|
|
732
|
+
: { valid: false, missingTags: [], isBraceError: false };
|
|
733
|
+
if (tagResult.valid) {
|
|
733
734
|
errorData[count][`sms-editor${index > 1 ? index : ''}`] = false;
|
|
734
735
|
} else {
|
|
735
|
-
|
|
736
|
-
const {
|
|
737
|
-
|
|
738
|
-
errorData[count][`
|
|
739
|
-
errorData[count][`bracket-error`] = tagValidationResponse.isBraceError && TAG_BRACKET_COUNT_MISMATCH_ERROR;
|
|
736
|
+
const { MISSING_TAG_ERROR, GENERIC_VALIDATION_ERROR, TAG_BRACKET_COUNT_MISMATCH_ERROR } = errorMessageForTags || {};
|
|
737
|
+
const { missingTags, isBraceError } = tagResult;
|
|
738
|
+
errorData[count][`sms-editor${index > 1 ? index : ''}`] = missingTags && missingTags.length ? MISSING_TAG_ERROR : (isBraceError ? TAG_BRACKET_COUNT_MISMATCH_ERROR : GENERIC_VALIDATION_ERROR);
|
|
739
|
+
errorData[count][`bracket-error`] = isBraceError && TAG_BRACKET_COUNT_MISMATCH_ERROR;
|
|
740
740
|
isValid = false;
|
|
741
741
|
}
|
|
742
742
|
if(content !== '' && (ifUnicode && !unicodeCheck)) {
|
|
@@ -764,7 +764,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
764
764
|
if (this.state.formData['message-editor'] !== undefined ) {
|
|
765
765
|
const content = this.state.formData['0']['message-editor'] || '';
|
|
766
766
|
|
|
767
|
-
const tagValidationResponse = this.validateTags((content), tags,
|
|
767
|
+
const tagValidationResponse = this.validateTags((content), tags, false, this.props?.isFullMode);
|
|
768
768
|
|
|
769
769
|
if (tagValidationResponse.valid) {
|
|
770
770
|
errorData = {
|
|
@@ -847,7 +847,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
847
847
|
errorData[index] = true;
|
|
848
848
|
isValid = false;
|
|
849
849
|
} else {
|
|
850
|
-
const tagValidationResponse = this.validateTags(content, tags,
|
|
850
|
+
const tagValidationResponse = this.validateTags(content, tags, false, this.props?.isFullMode);
|
|
851
851
|
|
|
852
852
|
if (tagValidationResponse.valid) {
|
|
853
853
|
errorData[index] = false;
|
|
@@ -910,14 +910,13 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
910
910
|
isCurrentTabValid = false;
|
|
911
911
|
} else {
|
|
912
912
|
errorData[parseInt(index)][`message-editor${selector}`] = false;
|
|
913
|
-
const tagValidationResponse = this.validateTags(message, tags,
|
|
913
|
+
const tagValidationResponse = this.validateTags(message, tags, false, this.props?.isFullMode);
|
|
914
914
|
|
|
915
915
|
if (tagValidationResponse.valid) {
|
|
916
916
|
errorData[parseInt(index)][`message-editor${selector}`] = false;
|
|
917
917
|
} else {
|
|
918
|
-
const {isBraceError} = tagValidationResponse;
|
|
918
|
+
const { isBraceError } = tagValidationResponse;
|
|
919
919
|
errorData[parseInt(index)][`message-editor${selector}`] = isBraceError ? TAG_BRACKET_COUNT_MISMATCH_ERROR : true;
|
|
920
|
-
errorData[parseInt(index)]['invalid-tags'] = tagValidationResponse.unsupportedTags;
|
|
921
920
|
errorData[parseInt(index)][`bracket-error`] = isBraceError && TAG_BRACKET_COUNT_MISMATCH_ERROR;
|
|
922
921
|
isValid = false;
|
|
923
922
|
}
|
|
@@ -939,7 +938,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
939
938
|
isCurrentTabValid = false;
|
|
940
939
|
} else {
|
|
941
940
|
errorData[parseInt(index)][`message-title${selector}`] = false;
|
|
942
|
-
const tagValidationResponse = this.validateTags(title, tags,
|
|
941
|
+
const tagValidationResponse = this.validateTags(title, tags, false, this.props?.isFullMode);
|
|
943
942
|
|
|
944
943
|
if (tagValidationResponse.valid) {
|
|
945
944
|
errorData[parseInt(index)][`message-title${selector}`] = false;
|
|
@@ -1193,8 +1192,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1193
1192
|
if (!content) {
|
|
1194
1193
|
return false;
|
|
1195
1194
|
}
|
|
1196
|
-
const tagValidationResponse = this.validateTags(content, tags,
|
|
1197
|
-
|
|
1195
|
+
const tagValidationResponse = this.validateTags(content, tags, isEmail, this.props?.isFullMode);
|
|
1198
1196
|
// Check for base64 images in email content
|
|
1199
1197
|
isEmail && containsBase64Images({content, callback:()=>{
|
|
1200
1198
|
tagValidationResponse.valid = false;
|
|
@@ -1210,23 +1208,20 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1210
1208
|
errorData[index][currentLang]['template-content'] = true;
|
|
1211
1209
|
isValid = false;
|
|
1212
1210
|
isLiquidValid = false;
|
|
1213
|
-
if ((showMessages && !isNaN(index)) || this.
|
|
1214
|
-
if (tagValidationResponse?.missingTags?.length > 0
|
|
1211
|
+
if ((showMessages && !isNaN(index)) || this.isLiquidFlowSupportedByChannel()) {
|
|
1212
|
+
if (tagValidationResponse?.missingTags?.length > 0) {
|
|
1215
1213
|
errorString += `${this.props.intl.formatMessage(messages.contentNotValidLanguage)} ${currentLang}\n`;
|
|
1216
1214
|
}
|
|
1217
1215
|
if (tagValidationResponse?.missingTags?.length > 0) {
|
|
1218
1216
|
errorString += `${this.props.intl.formatMessage(messages.missingTags)} ${tagValidationResponse.missingTags.toString()}\n`;
|
|
1219
1217
|
}
|
|
1220
|
-
if (tagValidationResponse?.unsupportedTags?.length > 0) {
|
|
1221
|
-
errorString += `${this.props.intl.formatMessage(messages.unsupportedTags)} ${tagValidationResponse.unsupportedTags.toString()}\n`;
|
|
1222
|
-
}
|
|
1223
1218
|
if (tagValidationResponse?.isBraceError){
|
|
1224
1219
|
errorString += this.props.intl.formatMessage(globalMessages.unbalanacedCurlyBraces);
|
|
1225
1220
|
}
|
|
1226
1221
|
if (tagValidationResponse?.isContentEmpty) {
|
|
1227
1222
|
errorString += this.props.intl.formatMessage(messages.emailBodyEmptyError);
|
|
1228
1223
|
// Adds a bypass for cases where content is initially empty in the creation flow.
|
|
1229
|
-
if(this.
|
|
1224
|
+
if(this.isLiquidFlowSupportedByChannel()){
|
|
1230
1225
|
errorString = "";
|
|
1231
1226
|
isLiquidValid = true;
|
|
1232
1227
|
}
|
|
@@ -1238,7 +1233,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1238
1233
|
}
|
|
1239
1234
|
}
|
|
1240
1235
|
if (errorString) {
|
|
1241
|
-
if (this.
|
|
1236
|
+
if (this.isLiquidFlowSupportedByChannel()) {
|
|
1242
1237
|
this.setState(
|
|
1243
1238
|
(prevState) => ({
|
|
1244
1239
|
liquidErrorMessage: {
|
|
@@ -1265,7 +1260,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1265
1260
|
});
|
|
1266
1261
|
}
|
|
1267
1262
|
|
|
1268
|
-
const isTemplateValid = this.
|
|
1263
|
+
const isTemplateValid = this.isLiquidFlowSupportedByChannel() ? isLiquidValid : isValid;
|
|
1269
1264
|
//Updating the state with the error data
|
|
1270
1265
|
this.setState((prevState) => ({
|
|
1271
1266
|
isFormValid: isTemplateValid,
|
|
@@ -1324,7 +1319,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1324
1319
|
}
|
|
1325
1320
|
onSubmitWrapper = (args) => {
|
|
1326
1321
|
const {singleTab = null} = args || {};
|
|
1327
|
-
if (this.
|
|
1322
|
+
if (this.isLiquidFlowSupportedByChannel()) {
|
|
1328
1323
|
// For MPUSH, we need to validate both Android and iOS content separately
|
|
1329
1324
|
if (this.props.channel === MOBILE_PUSH || this.props?.schema?.channel?.toUpperCase() === MOBILE_PUSH) {
|
|
1330
1325
|
this.validateFormBuilderMPush(this.state.formData, singleTab);
|
|
@@ -1368,10 +1363,6 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1368
1363
|
messages,
|
|
1369
1364
|
onError,
|
|
1370
1365
|
onSuccess,
|
|
1371
|
-
tagLookupMap: this.props?.metaEntities?.tagLookupMap,
|
|
1372
|
-
eventContextTags: this.props?.eventContextTags,
|
|
1373
|
-
isLiquidFlow: this.liquidFlow(),
|
|
1374
|
-
forwardedTags: this.props?.isLoyaltyModule ? this.props?.forwardedTags : {},
|
|
1375
1366
|
skipTags: this.skipTags.bind(this)
|
|
1376
1367
|
});
|
|
1377
1368
|
} else {
|
|
@@ -1440,13 +1431,6 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1440
1431
|
getLiquidTags: this.props.actions.getLiquidTags,
|
|
1441
1432
|
formatMessage: this.props.intl.formatMessage,
|
|
1442
1433
|
messages: messages,
|
|
1443
|
-
tagLookupMap: this.props?.metaEntities?.tagLookupMap,
|
|
1444
|
-
eventContextTags: this.props?.eventContextTags,
|
|
1445
|
-
isLiquidFlow: this.liquidFlow(), // Use the method instead of props
|
|
1446
|
-
forwardedTags: this.props?.isLoyaltyModule ? this.props?.forwardedTags : {},
|
|
1447
|
-
skipTags: this.skipTags.bind(this),
|
|
1448
|
-
extractNames,
|
|
1449
|
-
checkSupport,
|
|
1450
1434
|
singleTab: singleTab?.toUpperCase(),
|
|
1451
1435
|
});
|
|
1452
1436
|
}
|
|
@@ -1501,115 +1485,40 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1501
1485
|
});
|
|
1502
1486
|
}
|
|
1503
1487
|
|
|
1504
|
-
validateTags(content, tagsParam,
|
|
1505
|
-
const type = (this.props.location && this.props.location.query.type) ? this.props.location.query.type : 'full';
|
|
1488
|
+
validateTags(content, tagsParam, isEmail = false, isFullMode = this.props?.isFullMode) {
|
|
1506
1489
|
let currentModule = this.props.location.query.module ? this.props.location.query.module : 'default';
|
|
1507
1490
|
if (this.props.tagModule) {
|
|
1508
1491
|
currentModule = this.props.tagModule;
|
|
1509
1492
|
}
|
|
1510
1493
|
const tags = tagsParam ? tagsParam : this.props.tags;
|
|
1511
|
-
const
|
|
1512
|
-
const
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
response
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
if (tags && tags.length && !isFullMode) {
|
|
1529
|
-
_.forEach(tags, (tag) => {
|
|
1530
|
-
_.forEach(tag.definition.supportedModules, (module) => {
|
|
1531
|
-
if (module.mandatory && (currentModule === module.context)) {
|
|
1532
|
-
if (content.indexOf(`{{${tag.definition.value}}}`) === -1) {
|
|
1533
|
-
response.valid = false;
|
|
1534
|
-
response.missingTags.push(tag.definition.value);
|
|
1535
|
-
}
|
|
1536
|
-
}
|
|
1537
|
-
});
|
|
1538
|
-
});
|
|
1539
|
-
// Legacy Email (CK Editor): when unsubscribe is required, ensure we validate it even if tag schema didn't mark it mandatory for this module
|
|
1540
|
-
if (isEmail && !isEmailUnsubscribeTagMandatory() && isModuleTypeOutbound) {
|
|
1541
|
-
const hasUnsubscribeInContent = /{{unsubscribe(\(#[a-zA-Z\d]{6}\))?}}/g.test(content);
|
|
1542
|
-
if (!hasUnsubscribeInContent && response.missingTags.indexOf('unsubscribe') === -1) {
|
|
1543
|
-
response.valid = false;
|
|
1544
|
-
response.missingTags.push('unsubscribe');
|
|
1545
|
-
}
|
|
1546
|
-
}
|
|
1547
|
-
const regex = /{{[(A-Z\w+(\s\w+)*$\(\)@!#$%^&*~.,/\\]+}}/g;
|
|
1548
|
-
let match = regex.exec(content);
|
|
1549
|
-
const regexImgSrc=/<img[^>]*\bsrc\s*=\s*"[^"]*{{customer_barcode}}[^"]*"/;
|
|
1550
|
-
let matchImg = regexImgSrc.exec(content);
|
|
1551
|
-
const regexCustomerBarcode = /{{customer_barcode}}(?![^<]*>)/g;
|
|
1552
|
-
let matchCustomerBarcode = regexCustomerBarcode.exec(content);
|
|
1553
|
-
// \S matches anything other than a space, a tab, a newline, or a carriage return.
|
|
1554
|
-
const validString= /\S/.test(contentForValidation);
|
|
1555
|
-
if (isEmailUnsubscribeTagMandatory() && isEmail && isModuleTypeOutbound) {
|
|
1556
|
-
const missingTagIndex = response?.missingTags?.indexOf("unsubscribe");
|
|
1557
|
-
if(missingTagIndex != -1) { //skip regex tags for mandatory tags also
|
|
1558
|
-
response?.missingTags?.splice(missingTagIndex, 1);
|
|
1559
|
-
}
|
|
1560
|
-
if (validString) {
|
|
1561
|
-
response.valid = true;
|
|
1562
|
-
} else {
|
|
1563
|
-
response.isContentEmpty = true;
|
|
1564
|
-
}
|
|
1565
|
-
}
|
|
1566
|
-
while (match !== null ) {
|
|
1567
|
-
const tagValue = match[0].substring(this.indexOfEnd(match[0], '{{'), match[0].indexOf('}}'));
|
|
1568
|
-
const tagIndex = match?.index;
|
|
1569
|
-
match = regex.exec(content);
|
|
1570
|
-
let ifSupported = false;
|
|
1571
|
-
_.forEach(tags, (tag) => {
|
|
1572
|
-
if (tag.definition.value === tagValue) {
|
|
1573
|
-
ifSupported = true;
|
|
1574
|
-
}
|
|
1575
|
-
if(tagValue === CUSTOMER_BARCODE_TAG && (matchImg === null || matchCustomerBarcode !== null)){
|
|
1576
|
-
ifSupported = false;
|
|
1577
|
-
}
|
|
1578
|
-
});
|
|
1579
|
-
const ifSkipped = this.skipTags(tagValue);
|
|
1580
|
-
if (ifSkipped) {
|
|
1581
|
-
ifSupported = true;
|
|
1582
|
-
let isUnsubscribeSkipped = tagValue.indexOf("unsubscribe") != -1 ;
|
|
1583
|
-
if (isUnsubscribeSkipped) {
|
|
1584
|
-
const missingTagIndex = response.missingTags.indexOf("unsubscribe");
|
|
1585
|
-
if(missingTagIndex != -1) { //skip regex tags for mandatory tags also
|
|
1586
|
-
response.missingTags.splice(missingTagIndex, 1);
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1589
|
-
}
|
|
1590
|
-
|
|
1591
|
-
// Event Context Tags support
|
|
1592
|
-
this.props?.eventContextTags?.forEach((tag) => {
|
|
1593
|
-
if (tagValue === tag?.tagName) {
|
|
1594
|
-
ifSupported = true;
|
|
1595
|
-
}
|
|
1596
|
-
});
|
|
1597
|
-
|
|
1598
|
-
ifSupported = ifSupported || this.checkIfSupportedTag(tagValue, injectedTags);
|
|
1599
|
-
// Only add to unsupportedTags if not inside a {% ... %} block (scenario 3: liquid orgs also get unsupported-tag errors)
|
|
1600
|
-
if (!ifSupported && !isInsideLiquidBlock(content, tagIndex)) {
|
|
1601
|
-
response.unsupportedTags.push(tagValue);
|
|
1602
|
-
response.valid = false;
|
|
1603
|
-
}
|
|
1494
|
+
const contentForValidation = isEmail ? convert(content, GLOBAL_CONVERT_OPTIONS) : content;
|
|
1495
|
+
const isOutboundModule = (currentModule || '').toUpperCase() === OUTBOUND;
|
|
1496
|
+
|
|
1497
|
+
const initialMissingTags = (tags && tags.length && !isFullMode && isEmail && isOutboundModule && !isEmailUnsubscribeTagOptional() && !hasUnsubscribeTag(content))
|
|
1498
|
+
? ['unsubscribe']
|
|
1499
|
+
: [];
|
|
1500
|
+
|
|
1501
|
+
const response = validateTagsCore({
|
|
1502
|
+
contentForBraceCheck: contentForValidation,
|
|
1503
|
+
contentForUnsubscribeScan: content,
|
|
1504
|
+
tags,
|
|
1505
|
+
currentModule,
|
|
1506
|
+
isFullMode,
|
|
1507
|
+
initialMissingTags, // [] or ['unsubscribe']; core uses this instead of definition-based when provided
|
|
1508
|
+
skipTagsFn: this.skipTags.bind(this),
|
|
1509
|
+
includeIsContentEmpty: true,
|
|
1510
|
+
});
|
|
1604
1511
|
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1512
|
+
// When unsubscribe tag is optional (isEmailUnsubscribeTagOptional): do not enforce unsubscribe (defensive splice); set isContentEmpty only when content is empty. Do not override response.valid so brace/format errors are preserved.
|
|
1513
|
+
const validString = /\S/.test(contentForValidation);
|
|
1514
|
+
if (isEmailUnsubscribeTagOptional() && isEmail && isOutboundModule) {
|
|
1515
|
+
const missingTagIndex = response.missingTags.indexOf('unsubscribe');
|
|
1516
|
+
if (missingTagIndex !== -1) {
|
|
1517
|
+
response.missingTags.splice(missingTagIndex, 1);
|
|
1518
|
+
}
|
|
1519
|
+
if (!validString) {
|
|
1520
|
+
response.isContentEmpty = true;
|
|
1608
1521
|
}
|
|
1609
|
-
}
|
|
1610
|
-
if(!validateIfTagClosed(contentForValidation)){
|
|
1611
|
-
response.isBraceError = true;
|
|
1612
|
-
response.valid = false;
|
|
1613
1522
|
}
|
|
1614
1523
|
return response;
|
|
1615
1524
|
}
|
|
@@ -2843,21 +2752,21 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
2843
2752
|
|
|
2844
2753
|
|
|
2845
2754
|
getMissingOrUnsupportedTagsName = (content = '', type) => {
|
|
2846
|
-
const { MISSING_TAGS
|
|
2755
|
+
const { MISSING_TAGS } = tagsTypes;
|
|
2847
2756
|
const tagValidationResponse = this.validateTags(content);
|
|
2848
|
-
if (type &&
|
|
2849
|
-
return tagValidationResponse[type].join(', ').toString();
|
|
2757
|
+
if (type && type === MISSING_TAGS) {
|
|
2758
|
+
return (tagValidationResponse[type] || []).join(', ').toString();
|
|
2850
2759
|
}
|
|
2851
2760
|
return null;
|
|
2852
2761
|
};
|
|
2853
2762
|
|
|
2854
2763
|
renderTextAreaContent = (styling, columns, val, isVersionEnable, rows, cols, offset = 0) => {
|
|
2855
2764
|
const { checkValidation, errorData, currentTab, formData } = this.state;
|
|
2856
|
-
const { MISSING_TAGS
|
|
2765
|
+
const { MISSING_TAGS } = tagsTypes;
|
|
2857
2766
|
const errorType = (isVersionEnable ? errorData[`${currentTab - 1}`][val.id] : errorData[val.id]);
|
|
2858
2767
|
const ifError = checkValidation && errorType;
|
|
2859
2768
|
const messageContent = isVersionEnable ? formData[`${currentTab - 1}`][val.id] : formData[val.id];
|
|
2860
|
-
const { MISSING_TAG_ERROR,
|
|
2769
|
+
const { MISSING_TAG_ERROR, TAG_BRACKET_COUNT_MISMATCH_ERROR } = errorMessageForTags;
|
|
2861
2770
|
const { formatMessage } = this.props.intl;
|
|
2862
2771
|
|
|
2863
2772
|
const { accessibleFeatures = [] } = this.props.currentOrgDetails || {};
|
|
@@ -2870,9 +2779,6 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
2870
2779
|
case MISSING_TAG_ERROR:
|
|
2871
2780
|
errorMessageText = formatMessage(messages.missingTagsValidationError, {missingTags: this.getMissingOrUnsupportedTagsName(messageContent, MISSING_TAGS)});
|
|
2872
2781
|
break;
|
|
2873
|
-
case UNSUPPORTED_TAG_ERROR:
|
|
2874
|
-
errorMessageText = formatMessage(messages.unsupportedTagsValidationError, {unsupportedTags: this.getMissingOrUnsupportedTagsName(messageContent, UNSUPPORTED_TAGS)});
|
|
2875
|
-
break;
|
|
2876
2782
|
case TAG_BRACKET_COUNT_MISMATCH_ERROR:
|
|
2877
2783
|
errorMessageText = formatMessage(globalMessages.unbalanacedCurlyBraces);
|
|
2878
2784
|
break;
|
|
@@ -2883,7 +2789,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
2883
2789
|
break;
|
|
2884
2790
|
}
|
|
2885
2791
|
const prevErrorMessage = this.state.liquidErrorMessage?.STANDARD_ERROR_MSG?.[0];
|
|
2886
|
-
if (prevErrorMessage !== errorMessageText && errorMessageText && this.
|
|
2792
|
+
if (prevErrorMessage !== errorMessageText && errorMessageText && this.isLiquidFlowSupportedByChannel()) {
|
|
2887
2793
|
this.setState((prevState) => ({
|
|
2888
2794
|
liquidErrorMessage: {
|
|
2889
2795
|
...prevState.liquidErrorMessage,
|
|
@@ -2902,7 +2808,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
2902
2808
|
id={val.id}
|
|
2903
2809
|
placeholder={val.placeholder ? val.placeholder : ''}
|
|
2904
2810
|
className={`${ifError ? 'error-form-builder' : ''}`}
|
|
2905
|
-
errorMessage={errorMessageText && !this.
|
|
2811
|
+
errorMessage={errorMessageText && !this.isLiquidFlowSupportedByChannel() ? errorMessageText : ''}
|
|
2906
2812
|
label={val.label}
|
|
2907
2813
|
autosize={val.autosize ? val.autosizeParams : false}
|
|
2908
2814
|
onChange={(e) => this.updateFormData(e.target.value, val)}
|
|
@@ -3985,7 +3891,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3985
3891
|
<CapColumn
|
|
3986
3892
|
style={val.colStyle ? val.colStyle : {border : ""}}
|
|
3987
3893
|
span={val.width}
|
|
3988
|
-
className={
|
|
3894
|
+
className={(this.state.liquidErrorMessage?.LIQUID_ERROR_MSG?.length || this.state.liquidErrorMessage?.STANDARD_ERROR_MSG?.length) && this.isLiquidFlowSupportedByChannel() ? "error-boundary" : ""}
|
|
3989
3895
|
>
|
|
3990
3896
|
<CKEditor
|
|
3991
3897
|
id={val.id}
|
|
@@ -4029,7 +3935,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
4029
3935
|
isModuleFilterEnabled = this.props.isFullMode;
|
|
4030
3936
|
}
|
|
4031
3937
|
columns.push(
|
|
4032
|
-
<CapColumn style={val.colStyle ? val.colStyle : {}} span={val.width} className={
|
|
3938
|
+
<CapColumn style={val.colStyle ? val.colStyle : {}} span={val.width} className={(this.state.liquidErrorMessage?.LIQUID_ERROR_MSG?.length || this.state.liquidErrorMessage?.STANDARD_ERROR_MSG?.length) && this.isLiquidFlowSupportedByChannel() ? "error-boundary" : ""}>
|
|
4033
3939
|
<BeeEditor
|
|
4034
3940
|
uid={uuid}
|
|
4035
3941
|
tokenData={beeToken}
|
|
@@ -4315,7 +4221,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
4315
4221
|
|
|
4316
4222
|
|
|
4317
4223
|
return (
|
|
4318
|
-
<CapSpin spinning={Boolean((this.
|
|
4224
|
+
<CapSpin spinning={Boolean((this.isLiquidFlowSupportedByChannel() && this.props.liquidExtractionInProgress) || this.props.metaDataStatus === REQUEST)} tip={this.props.intl.formatMessage(messages.liquidSpinText)} >
|
|
4319
4225
|
<CapRow>
|
|
4320
4226
|
{this.props.schema && this.renderForm()}
|
|
4321
4227
|
<SlideBox
|
|
@@ -26,10 +26,6 @@ export default defineMessages({
|
|
|
26
26
|
id: 'creatives.components.FormBuilder.missingTagsValidationError',
|
|
27
27
|
defaultMessage: 'Missing tags: {missingTags}. Please add them to this message.',
|
|
28
28
|
},
|
|
29
|
-
unsupportedTagsValidationError: {
|
|
30
|
-
id: 'creatives.components.FormBuilder.unsupportedTagsValidationError',
|
|
31
|
-
defaultMessage: 'Unsupported tags: {unsupportedTags}. Please remove them from this message.',
|
|
32
|
-
},
|
|
33
29
|
genericTagsValidationError: {
|
|
34
30
|
id: 'creatives.components.FormBuilder.genericTagsValidationError',
|
|
35
31
|
defaultMessage: 'Please check the message content for unsupported/missing tags',
|
|
@@ -42,10 +38,6 @@ export default defineMessages({
|
|
|
42
38
|
id: 'creatives.componentsV2.FormBuilder.missingTags',
|
|
43
39
|
defaultMessage: 'Missing tags are:',
|
|
44
40
|
},
|
|
45
|
-
unsupportedTags: {
|
|
46
|
-
id: 'creatives.componentsV2.FormBuilder.unsupportedTags',
|
|
47
|
-
defaultMessage: 'Unsupported tags are:',
|
|
48
|
-
},
|
|
49
41
|
upload: {
|
|
50
42
|
id: 'creatives.componentsV2.FormBuilder.upload',
|
|
51
43
|
defaultMessage: 'Upload',
|
|
@@ -102,7 +102,6 @@ const HTMLEditor = forwardRef(({
|
|
|
102
102
|
onTagSelect = null,
|
|
103
103
|
onContextChange = null,
|
|
104
104
|
globalActions = null,
|
|
105
|
-
isLiquidEnabled = false, // Controls Liquid tab visibility in ValidationTabs
|
|
106
105
|
isFullMode = true, // Full mode vs library mode - controls layout and visibility
|
|
107
106
|
onErrorAcknowledged = null, // Callback when user clicks redirection icon to acknowledge errors
|
|
108
107
|
onValidationChange = null, // Callback when validation state changes (for parent to track errors)
|
|
@@ -573,7 +572,6 @@ const HTMLEditor = forwardRef(({
|
|
|
573
572
|
content,
|
|
574
573
|
layout,
|
|
575
574
|
validation,
|
|
576
|
-
isLiquidEnabled,
|
|
577
575
|
editorRef: getActiveEditorRef(),
|
|
578
576
|
handleLabelInsert,
|
|
579
577
|
handleSave,
|
|
@@ -593,7 +591,6 @@ const HTMLEditor = forwardRef(({
|
|
|
593
591
|
content,
|
|
594
592
|
layout,
|
|
595
593
|
validation,
|
|
596
|
-
isLiquidEnabled,
|
|
597
594
|
getActiveEditorRef,
|
|
598
595
|
handleLabelInsert,
|
|
599
596
|
handleSave,
|
|
@@ -782,7 +779,6 @@ HTMLEditor.propTypes = {
|
|
|
782
779
|
onTagSelect: PropTypes.func,
|
|
783
780
|
onContextChange: PropTypes.func, // Deprecated: use globalActions instead
|
|
784
781
|
globalActions: PropTypes.object,
|
|
785
|
-
isLiquidEnabled: PropTypes.bool, // Controls Liquid tab visibility in validation
|
|
786
782
|
isFullMode: PropTypes.bool, // Full mode vs library mode
|
|
787
783
|
onErrorAcknowledged: PropTypes.func, // Callback when user clicks redirection icon to acknowledge errors
|
|
788
784
|
onValidationChange: PropTypes.func, // Callback when validation state changes
|
|
@@ -816,7 +812,6 @@ HTMLEditor.defaultProps = {
|
|
|
816
812
|
onTagSelect: null,
|
|
817
813
|
onContextChange: null,
|
|
818
814
|
globalActions: null, // Redux actions for API calls
|
|
819
|
-
isLiquidEnabled: false,
|
|
820
815
|
isFullMode: true, // Default to full mode
|
|
821
816
|
onErrorAcknowledged: null, // Callback when user clicks redirection icon to acknowledge errors
|
|
822
817
|
onValidationChange: null, // Callback when validation state changes
|
|
@@ -3201,7 +3201,6 @@ describe('HTMLEditor', () => {
|
|
|
3201
3201
|
onTagSelect={onTagSelect}
|
|
3202
3202
|
onContextChange={onContextChange}
|
|
3203
3203
|
globalActions={globalActions}
|
|
3204
|
-
isLiquidEnabled={true}
|
|
3205
3204
|
isFullMode={false}
|
|
3206
3205
|
onErrorAcknowledged={onErrorAcknowledged}
|
|
3207
3206
|
onValidationChange={onValidationChange}
|
|
@@ -3261,20 +3260,6 @@ describe('HTMLEditor', () => {
|
|
|
3261
3260
|
expect(screen.getByTestId('device-toggle')).toBeInTheDocument();
|
|
3262
3261
|
});
|
|
3263
3262
|
|
|
3264
|
-
it('should handle isLiquidEnabled prop', () => {
|
|
3265
|
-
render(
|
|
3266
|
-
<TestWrapper>
|
|
3267
|
-
<HTMLEditor isLiquidEnabled={true} />
|
|
3268
|
-
</TestWrapper>
|
|
3269
|
-
);
|
|
3270
|
-
|
|
3271
|
-
act(() => {
|
|
3272
|
-
jest.runAllTimers();
|
|
3273
|
-
});
|
|
3274
|
-
|
|
3275
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3276
|
-
});
|
|
3277
|
-
|
|
3278
3263
|
it('should handle isFullMode prop', () => {
|
|
3279
3264
|
render(
|
|
3280
3265
|
<TestWrapper>
|
|
@@ -69,7 +69,7 @@ const CodeEditorPaneComponent = ({
|
|
|
69
69
|
}) => {
|
|
70
70
|
const context = useEditorContext();
|
|
71
71
|
const {
|
|
72
|
-
content, validation, variant,
|
|
72
|
+
content, validation, variant,
|
|
73
73
|
} = context || {};
|
|
74
74
|
const { content: contentValue, updateContent } = content || {};
|
|
75
75
|
const editorRef = useRef(null);
|
|
@@ -298,7 +298,6 @@ const CodeEditorPaneComponent = ({
|
|
|
298
298
|
<ValidationErrorDisplay
|
|
299
299
|
validation={validation}
|
|
300
300
|
onErrorClick={onErrorClick}
|
|
301
|
-
isLiquidEnabled={isLiquidEnabled}
|
|
302
301
|
className="code-editor-pane__validation"
|
|
303
302
|
/>
|
|
304
303
|
</div>
|
|
@@ -2,11 +2,9 @@ export const expectedStateGetLiquidTagsRequest = {
|
|
|
2
2
|
fetchingLiquidTags: true,
|
|
3
3
|
fetchingSchema: true,
|
|
4
4
|
fetchingSchemaError: "",
|
|
5
|
-
liquidTags: [],
|
|
6
5
|
messages: [],
|
|
7
6
|
metaEntities: {
|
|
8
7
|
layouts: [],
|
|
9
|
-
tagLookupMap: {},
|
|
10
8
|
tags: [],
|
|
11
9
|
},
|
|
12
10
|
orgID: "",
|
|
@@ -17,11 +15,9 @@ export const expectedStateGetLiquidTagsFailure = {
|
|
|
17
15
|
fetchingLiquidTags: false,
|
|
18
16
|
fetchingSchema: true,
|
|
19
17
|
fetchingSchemaError: "",
|
|
20
|
-
liquidTags: [],
|
|
21
18
|
messages: [],
|
|
22
19
|
metaEntities: {
|
|
23
20
|
layouts: [],
|
|
24
|
-
tagLookupMap: {},
|
|
25
21
|
tags: [],
|
|
26
22
|
},
|
|
27
23
|
orgID: "",
|
|
@@ -32,11 +28,9 @@ export const expectedStateGetLiquidTagsSuccess = {
|
|
|
32
28
|
fetchingLiquidTags: false,
|
|
33
29
|
fetchingSchema: true,
|
|
34
30
|
fetchingSchemaError: "",
|
|
35
|
-
liquidTags: [],
|
|
36
31
|
messages: [],
|
|
37
32
|
metaEntities: {
|
|
38
33
|
layouts: [],
|
|
39
|
-
tagLookupMap: {},
|
|
40
34
|
tags: [],
|
|
41
35
|
},
|
|
42
36
|
orgID: "",
|
|
@@ -47,11 +41,9 @@ export const expectedStateGetSchemaForEntitySuccessTAG = {
|
|
|
47
41
|
fetchingLiquidTags: false,
|
|
48
42
|
fetchingSchema: false,
|
|
49
43
|
fetchingSchemaError: false,
|
|
50
|
-
liquidTags: [],
|
|
51
44
|
messages: [],
|
|
52
45
|
metaEntities: {
|
|
53
46
|
layouts: undefined,
|
|
54
|
-
tagLookupMap: { undefined: { definition: {} } },
|
|
55
47
|
tags: { standard: { random: "32" } },
|
|
56
48
|
},
|
|
57
49
|
orgID: "",
|
|
@@ -62,11 +54,9 @@ export const expectedStateGetSchemaForEntitySuccess = {
|
|
|
62
54
|
fetchingLiquidTags: false,
|
|
63
55
|
fetchingSchema: false,
|
|
64
56
|
fetchingSchemaError: false,
|
|
65
|
-
liquidTags: [],
|
|
66
57
|
messages: [],
|
|
67
58
|
metaEntities: {
|
|
68
59
|
layouts: undefined,
|
|
69
|
-
tagLookupMap: undefined,
|
|
70
60
|
tags: undefined,
|
|
71
61
|
},
|
|
72
62
|
orgID: "",
|
|
@@ -78,13 +68,9 @@ export const expectedForwardedTags = {
|
|
|
78
68
|
fetchingSchema: false,
|
|
79
69
|
fetchingSchemaError: '',
|
|
80
70
|
injectedTags: undefined,
|
|
81
|
-
liquidTags: [],
|
|
82
71
|
messages: [],
|
|
83
72
|
metaEntities: {
|
|
84
73
|
layouts: [],
|
|
85
|
-
tagLookupMap: {
|
|
86
|
-
|
|
87
|
-
},
|
|
88
74
|
tags: [],
|
|
89
75
|
},
|
|
90
76
|
orgID: "",
|