@capillarytech/creatives-library 8.0.309 → 8.0.310
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 +1 -5
- package/initialState.js +2 -0
- package/package.json +1 -1
- package/services/api.js +0 -17
- package/services/tests/api.test.js +0 -85
- package/utils/common.js +8 -5
- package/utils/commonUtils.js +93 -46
- package/utils/tagValidations.js +223 -83
- package/utils/tests/commonUtil.test.js +124 -316
- package/utils/tests/tagValidations.test.js +358 -441
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +49 -78
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +34 -134
- package/v2Components/CommonTestAndPreview/actions.js +0 -10
- package/v2Components/CommonTestAndPreview/constants.js +1 -15
- package/v2Components/CommonTestAndPreview/index.js +19 -80
- package/v2Components/CommonTestAndPreview/messages.js +0 -94
- package/v2Components/CommonTestAndPreview/reducer.js +0 -10
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +0 -53
- package/v2Components/CommonTestAndPreview/tests/constants.test.js +1 -31
- package/v2Components/CommonTestAndPreview/tests/index.test.js +0 -36
- package/v2Components/CommonTestAndPreview/tests/reducer.test.js +0 -71
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +0 -377
- package/v2Components/CommonTestAndPreview/tests/selectors.test.js +0 -17
- package/v2Components/ErrorInfoNote/index.js +5 -2
- package/v2Components/FormBuilder/index.js +203 -137
- package/v2Components/FormBuilder/messages.js +8 -0
- package/v2Components/HtmlEditor/HTMLEditor.js +5 -0
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -0
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +15 -0
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +2 -1
- package/v2Containers/Cap/mockData.js +14 -0
- package/v2Containers/Cap/reducer.js +55 -3
- package/v2Containers/Cap/tests/reducer.test.js +102 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +1 -5
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +5 -13
- package/v2Containers/CreativesContainer/constants.js +0 -6
- package/v2Containers/CreativesContainer/index.js +7 -47
- package/v2Containers/Email/index.js +5 -1
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +70 -23
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +120 -20
- package/v2Containers/FTP/index.js +51 -2
- package/v2Containers/FTP/messages.js +4 -0
- package/v2Containers/InApp/index.js +107 -35
- package/v2Containers/InApp/tests/index.test.js +6 -17
- package/v2Containers/InappAdvance/index.js +112 -4
- package/v2Containers/InappAdvance/tests/index.test.js +0 -2
- package/v2Containers/Line/Container/Text/index.js +1 -0
- package/v2Containers/MobilePush/Create/index.js +19 -59
- package/v2Containers/MobilePush/Edit/index.js +20 -48
- package/v2Containers/MobilePushNew/index.js +32 -12
- package/v2Containers/MobilepushWrapper/index.js +1 -3
- package/v2Containers/Rcs/index.js +37 -12
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +1276 -1408
- package/v2Containers/Sms/Create/index.js +3 -39
- package/v2Containers/Sms/Create/messages.js +0 -4
- package/v2Containers/Sms/Edit/index.js +3 -35
- package/v2Containers/Sms/commonMethods.js +6 -3
- package/v2Containers/SmsTrai/Edit/index.js +47 -11
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +294 -327
- package/v2Containers/SmsWrapper/index.js +0 -2
- package/v2Containers/TemplatesV2/index.js +13 -28
- package/v2Containers/Viber/index.js +1 -0
- package/v2Containers/WebPush/Create/hooks/useTagManagement.js +3 -1
- package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +7 -0
- package/v2Containers/WebPush/Create/index.js +2 -2
- package/v2Containers/WebPush/Create/utils/validation.js +8 -17
- package/v2Containers/WebPush/Create/utils/validation.test.js +24 -44
- package/v2Containers/Whatsapp/index.js +17 -9
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +4872 -5246
- package/v2Containers/Zalo/index.js +11 -3
- package/v2Components/CommonTestAndPreview/AddTestCustomer.js +0 -42
- package/v2Components/CommonTestAndPreview/CustomerCreationModal.js +0 -284
- package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +0 -72
- package/v2Components/CommonTestAndPreview/tests/AddTestCustomer.test.js +0 -66
- package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +0 -657
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +0 -172
- package/v2Components/CommonTestAndPreview/tests/CustomerCreationModal.test.js +0 -466
- package/v2Components/CommonTestAndPreview/tests/ExistingCustomerModal.test.js +0 -114
- package/v2Containers/Sms/tests/commonMethods.test.js +0 -122
|
@@ -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 { preprocessHtml,
|
|
53
|
+
import { checkSupport, extractNames, preprocessHtml, validateIfTagClosed, isInsideLiquidBlock} 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 { hasLiquidSupportFeature, isEmailUnsubscribeTagMandatory } from '../../utils/common';
|
|
64
64
|
import { isUrl } from '../../v2Containers/Line/Container/Wrapper/utils';
|
|
65
65
|
import { bindActionCreators } from 'redux';
|
|
66
66
|
import { getChannelData, hasPersonalizationTags, validateLiquidTemplateContent, validateMobilePushContent } from '../../utils/commonUtils';
|
|
@@ -72,8 +72,10 @@ const {CapRadioGroup} = CapRadio;
|
|
|
72
72
|
|
|
73
73
|
const tagsTypes = {
|
|
74
74
|
MISSING_TAGS: 'missingTags',
|
|
75
|
+
UNSUPPORTED_TAGS: 'unsupportedTags',
|
|
75
76
|
};
|
|
76
77
|
const errorMessageForTags = {
|
|
78
|
+
UNSUPPORTED_TAG_ERROR: 'unsupportedTagsError',
|
|
77
79
|
MISSING_TAG_ERROR: 'missingTagsError',
|
|
78
80
|
GENERIC_VALIDATION_ERROR: 'genericValidationError',
|
|
79
81
|
TAG_BRACKET_COUNT_MISMATCH_ERROR: 'tagBracketCountMismatchError'
|
|
@@ -136,7 +138,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
136
138
|
this.handleSetRadioValue = this.handleSetRadioValue.bind(this);
|
|
137
139
|
this.formElements = [];
|
|
138
140
|
// Check if the liquid flow feature is supported and the channel is in the supported list.
|
|
139
|
-
this.
|
|
141
|
+
this.liquidFlow = this.isLiquidFlowSupported.bind(this);
|
|
140
142
|
this.onSubmitWrapper = this.onSubmitWrapper.bind(this);
|
|
141
143
|
|
|
142
144
|
// Performance optimization: Debounced functions for high-frequency updates
|
|
@@ -328,8 +330,8 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
328
330
|
return updatedFormData;
|
|
329
331
|
}
|
|
330
332
|
|
|
331
|
-
|
|
332
|
-
return Boolean(LIQUID_SUPPORTED_CHANNELS.includes(this.props?.schema?.channel?.toUpperCase()));
|
|
333
|
+
isLiquidFlowSupported = () => {
|
|
334
|
+
return Boolean(LIQUID_SUPPORTED_CHANNELS.includes(this.props?.schema?.channel?.toUpperCase()) && hasLiquidSupportFeature());
|
|
333
335
|
}
|
|
334
336
|
|
|
335
337
|
componentWillMount() {
|
|
@@ -712,9 +714,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
712
714
|
if (channel && channel.toUpperCase() === SMS) {
|
|
713
715
|
for (let count = 0; count < this.state.tabCount; count += 1) {
|
|
714
716
|
if (_.isEmpty(errorData[count])) {
|
|
715
|
-
|
|
716
|
-
// prevents onFormValidityChange from firing, which makes Done appear unresponsive.
|
|
717
|
-
errorData[count] = {};
|
|
717
|
+
return;
|
|
718
718
|
}
|
|
719
719
|
const index = count + 1;
|
|
720
720
|
if (!this.state.formData[count]) {
|
|
@@ -726,19 +726,17 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
726
726
|
|
|
727
727
|
let tagValidationResponse = false;
|
|
728
728
|
if (content) {
|
|
729
|
-
tagValidationResponse = this.validateTags(content, tags, false, this.props?.isFullMode);
|
|
729
|
+
tagValidationResponse = this.validateTags(content, tags, injectedTags, false, this.props?.isFullMode);
|
|
730
730
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
? tagValidationResponse
|
|
734
|
-
: { valid: false, missingTags: [], isBraceError: false };
|
|
735
|
-
if (tagResult.valid) {
|
|
731
|
+
|
|
732
|
+
if (tagValidationResponse.valid) {
|
|
736
733
|
errorData[count][`sms-editor${index > 1 ? index : ''}`] = false;
|
|
737
734
|
} else {
|
|
738
|
-
|
|
739
|
-
const {
|
|
740
|
-
|
|
741
|
-
errorData[count][`
|
|
735
|
+
errorData[count]['invalid-tags'] = tagValidationResponse.unsupportedTags;
|
|
736
|
+
const { MISSING_TAG_ERROR, UNSUPPORTED_TAG_ERROR, GENERIC_VALIDATION_ERROR, TAG_BRACKET_COUNT_MISMATCH_ERROR } = errorMessageForTags || {};
|
|
737
|
+
const { missingTags, unsupportedTags, isBraceError} = tagValidationResponse;
|
|
738
|
+
errorData[count][`sms-editor${index > 1 ? index : ''}`] = missingTags && missingTags.length ? MISSING_TAG_ERROR : ( unsupportedTags && unsupportedTags.length ? UNSUPPORTED_TAG_ERROR : (isBraceError ? TAG_BRACKET_COUNT_MISMATCH_ERROR : GENERIC_VALIDATION_ERROR));
|
|
739
|
+
errorData[count][`bracket-error`] = tagValidationResponse.isBraceError && TAG_BRACKET_COUNT_MISMATCH_ERROR;
|
|
742
740
|
isValid = false;
|
|
743
741
|
}
|
|
744
742
|
if(content !== '' && (ifUnicode && !unicodeCheck)) {
|
|
@@ -766,7 +764,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
766
764
|
if (this.state.formData['message-editor'] !== undefined ) {
|
|
767
765
|
const content = this.state.formData['0']['message-editor'] || '';
|
|
768
766
|
|
|
769
|
-
const tagValidationResponse = this.validateTags((content), tags, false, this.props?.isFullMode);
|
|
767
|
+
const tagValidationResponse = this.validateTags((content), tags, injectedTags, false, this.props?.isFullMode);
|
|
770
768
|
|
|
771
769
|
if (tagValidationResponse.valid) {
|
|
772
770
|
errorData = {
|
|
@@ -849,7 +847,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
849
847
|
errorData[index] = true;
|
|
850
848
|
isValid = false;
|
|
851
849
|
} else {
|
|
852
|
-
const tagValidationResponse = this.validateTags(content, tags, false, this.props?.isFullMode);
|
|
850
|
+
const tagValidationResponse = this.validateTags(content, tags, injectedTags, false, this.props?.isFullMode);
|
|
853
851
|
|
|
854
852
|
if (tagValidationResponse.valid) {
|
|
855
853
|
errorData[index] = false;
|
|
@@ -915,13 +913,14 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
915
913
|
isCurrentTabValid = false;
|
|
916
914
|
} else {
|
|
917
915
|
errorData[parseInt(index)][`message-editor${selector}`] = false;
|
|
918
|
-
const tagValidationResponse = this.validateTags(message, tags, false, this.props?.isFullMode);
|
|
916
|
+
const tagValidationResponse = this.validateTags(message, tags, injectedTags, false, this.props?.isFullMode);
|
|
919
917
|
|
|
920
918
|
if (tagValidationResponse.valid) {
|
|
921
919
|
errorData[parseInt(index)][`message-editor${selector}`] = false;
|
|
922
920
|
} else {
|
|
923
|
-
const {
|
|
921
|
+
const {isBraceError} = tagValidationResponse;
|
|
924
922
|
errorData[parseInt(index)][`message-editor${selector}`] = isBraceError ? TAG_BRACKET_COUNT_MISMATCH_ERROR : true;
|
|
923
|
+
errorData[parseInt(index)]['invalid-tags'] = tagValidationResponse.unsupportedTags;
|
|
925
924
|
errorData[parseInt(index)][`bracket-error`] = isBraceError && TAG_BRACKET_COUNT_MISMATCH_ERROR;
|
|
926
925
|
isValid = false;
|
|
927
926
|
}
|
|
@@ -947,7 +946,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
947
946
|
isCurrentTabValid = false;
|
|
948
947
|
} else {
|
|
949
948
|
errorData[parseInt(index)][`message-title${selector}`] = false;
|
|
950
|
-
const tagValidationResponse = this.validateTags(title, tags, false, this.props?.isFullMode);
|
|
949
|
+
const tagValidationResponse = this.validateTags(title, tags, injectedTags, false, this.props?.isFullMode);
|
|
951
950
|
|
|
952
951
|
if (tagValidationResponse.valid) {
|
|
953
952
|
errorData[parseInt(index)][`message-title${selector}`] = false;
|
|
@@ -1201,7 +1200,8 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1201
1200
|
if (!content) {
|
|
1202
1201
|
return false;
|
|
1203
1202
|
}
|
|
1204
|
-
const tagValidationResponse = this.validateTags(content, tags, isEmail, this.props?.isFullMode);
|
|
1203
|
+
const tagValidationResponse = this.validateTags(content, tags, injectedTags, isEmail, this.props?.isFullMode);
|
|
1204
|
+
|
|
1205
1205
|
// Check for base64 images in email content
|
|
1206
1206
|
isEmail && containsBase64Images({content, callback:()=>{
|
|
1207
1207
|
tagValidationResponse.valid = false;
|
|
@@ -1217,20 +1217,23 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1217
1217
|
errorData[index][currentLang]['template-content'] = true;
|
|
1218
1218
|
isValid = false;
|
|
1219
1219
|
isLiquidValid = false;
|
|
1220
|
-
if ((showMessages && !isNaN(index)) || this.
|
|
1221
|
-
if (tagValidationResponse?.missingTags?.length > 0) {
|
|
1220
|
+
if ((showMessages && !isNaN(index)) || this.liquidFlow()) {
|
|
1221
|
+
if (tagValidationResponse?.missingTags?.length > 0 || tagValidationResponse?.unsupportedTags?.length > 0) {
|
|
1222
1222
|
errorString += `${this.props.intl.formatMessage(messages.contentNotValidLanguage)} ${currentLang}\n`;
|
|
1223
1223
|
}
|
|
1224
1224
|
if (tagValidationResponse?.missingTags?.length > 0) {
|
|
1225
1225
|
errorString += `${this.props.intl.formatMessage(messages.missingTags)} ${tagValidationResponse.missingTags.toString()}\n`;
|
|
1226
1226
|
}
|
|
1227
|
+
if (tagValidationResponse?.unsupportedTags?.length > 0) {
|
|
1228
|
+
errorString += `${this.props.intl.formatMessage(messages.unsupportedTags)} ${tagValidationResponse.unsupportedTags.toString()}\n`;
|
|
1229
|
+
}
|
|
1227
1230
|
if (tagValidationResponse?.isBraceError){
|
|
1228
1231
|
errorString += this.props.intl.formatMessage(globalMessages.unbalanacedCurlyBraces);
|
|
1229
1232
|
}
|
|
1230
1233
|
if (tagValidationResponse?.isContentEmpty) {
|
|
1231
1234
|
errorString += this.props.intl.formatMessage(messages.emailBodyEmptyError);
|
|
1232
1235
|
// Adds a bypass for cases where content is initially empty in the creation flow.
|
|
1233
|
-
if(this.
|
|
1236
|
+
if(this.liquidFlow()){
|
|
1234
1237
|
errorString = "";
|
|
1235
1238
|
isLiquidValid = true;
|
|
1236
1239
|
}
|
|
@@ -1242,7 +1245,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1242
1245
|
}
|
|
1243
1246
|
}
|
|
1244
1247
|
if (errorString) {
|
|
1245
|
-
if (this.
|
|
1248
|
+
if (this.liquidFlow()) {
|
|
1246
1249
|
this.setState(
|
|
1247
1250
|
(prevState) => ({
|
|
1248
1251
|
liquidErrorMessage: {
|
|
@@ -1254,11 +1257,8 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1254
1257
|
this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage, this.props.channel === SMS? null: this.state.currentTab);
|
|
1255
1258
|
}
|
|
1256
1259
|
);
|
|
1257
|
-
//
|
|
1258
|
-
|
|
1259
|
-
if (!isEmailChannel) {
|
|
1260
|
-
this.openNotificationWithIcon('error', errorString, 'email-validation-error');
|
|
1261
|
-
}
|
|
1260
|
+
// Show toast for liquid flow too so user sees error (scenario 3)
|
|
1261
|
+
this.openNotificationWithIcon('error', errorString, "email-validation-error");
|
|
1262
1262
|
} else {
|
|
1263
1263
|
this.openNotificationWithIcon('error', errorString, "email-validation-error");
|
|
1264
1264
|
}
|
|
@@ -1272,7 +1272,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1272
1272
|
});
|
|
1273
1273
|
}
|
|
1274
1274
|
|
|
1275
|
-
const isTemplateValid = this.
|
|
1275
|
+
const isTemplateValid = this.liquidFlow() ? isLiquidValid : isValid;
|
|
1276
1276
|
//Updating the state with the error data
|
|
1277
1277
|
this.setState((prevState) => ({
|
|
1278
1278
|
isFormValid: isTemplateValid,
|
|
@@ -1331,59 +1331,55 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1331
1331
|
}
|
|
1332
1332
|
onSubmitWrapper = (args) => {
|
|
1333
1333
|
const {singleTab = null} = args || {};
|
|
1334
|
-
|
|
1335
|
-
const runLiquidValidation = this.isLiquidFlowSupportedByChannel() && !this.props.isFullMode;
|
|
1336
|
-
if (runLiquidValidation) {
|
|
1334
|
+
if (this.liquidFlow() && !this.props?.isFullMode) {
|
|
1337
1335
|
// For MPUSH, we need to validate both Android and iOS content separately
|
|
1338
1336
|
if (this.props.channel === MOBILE_PUSH || this.props?.schema?.channel?.toUpperCase() === MOBILE_PUSH) {
|
|
1339
1337
|
this.validateFormBuilderMPush(this.state.formData, singleTab);
|
|
1340
1338
|
return;
|
|
1341
1339
|
}
|
|
1342
|
-
|
|
1343
|
-
// For other channels (EMAIL, SMS, INAPP)
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
},
|
|
1360
|
-
}),
|
|
1361
|
-
() => {
|
|
1362
|
-
this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage);
|
|
1363
|
-
this.props.stopValidation();
|
|
1364
|
-
this.props.onFormValidityChange(false, this.state.errorData);
|
|
1365
|
-
}
|
|
1366
|
-
);
|
|
1367
|
-
};
|
|
1368
|
-
|
|
1369
|
-
const onSuccess = (contentToSubmit) => {
|
|
1370
|
-
const channel = this.props.channel || this.props?.schema?.channel?.toUpperCase();
|
|
1371
|
-
if(channel === EMAIL) {
|
|
1372
|
-
const content = this.state.formData?.base?.[this.props.baseLanguage]?.["template-content"] || "";
|
|
1373
|
-
this.handleLiquidTemplateSubmit(content);
|
|
1374
|
-
} else {
|
|
1375
|
-
this.handleLiquidTemplateSubmit(contentToSubmit);
|
|
1340
|
+
|
|
1341
|
+
// For other channels (EMAIL, SMS, INAPP)
|
|
1342
|
+
const content = getChannelData(this.props.schema.channel || this.props.channel, this.state.formData, this.props.baseLanguage);
|
|
1343
|
+
|
|
1344
|
+
// Set up callbacks for error and success handling
|
|
1345
|
+
const onError = ({ standardErrors, liquidErrors }) => {
|
|
1346
|
+
this.setState(
|
|
1347
|
+
(prevState) => ({
|
|
1348
|
+
liquidErrorMessage: {
|
|
1349
|
+
...prevState.liquidErrorMessage,
|
|
1350
|
+
STANDARD_ERROR_MSG: standardErrors,
|
|
1351
|
+
LIQUID_ERROR_MSG: liquidErrors,
|
|
1352
|
+
},
|
|
1353
|
+
}),
|
|
1354
|
+
() => {
|
|
1355
|
+
this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage);
|
|
1356
|
+
this.props.stopValidation();
|
|
1376
1357
|
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1358
|
+
);
|
|
1359
|
+
};
|
|
1360
|
+
|
|
1361
|
+
const onSuccess = (contentToSubmit) => {
|
|
1362
|
+
const channel = this.props.channel || this.props?.schema?.channel?.toUpperCase();
|
|
1363
|
+
if(channel === EMAIL) {
|
|
1364
|
+
const content = this.state.formData?.base?.[this.props.baseLanguage]?.["template-content"] || "";
|
|
1365
|
+
this.handleLiquidTemplateSubmit(content);
|
|
1366
|
+
} else {
|
|
1367
|
+
this.handleLiquidTemplateSubmit(contentToSubmit);
|
|
1368
|
+
}
|
|
1369
|
+
};
|
|
1370
|
+
|
|
1371
|
+
// Call the common validation function
|
|
1372
|
+
validateLiquidTemplateContent(content, {
|
|
1373
|
+
getLiquidTags: this.props.actions.getLiquidTags,
|
|
1374
|
+
formatMessage: this.props.intl.formatMessage,
|
|
1375
|
+
messages,
|
|
1376
|
+
onError,
|
|
1377
|
+
onSuccess,
|
|
1378
|
+
tagLookupMap: this.props?.metaEntities?.tagLookupMap,
|
|
1379
|
+
eventContextTags: this.props?.eventContextTags,
|
|
1380
|
+
isLiquidFlow: this.liquidFlow(),
|
|
1381
|
+
forwardedTags: this.props?.isLoyaltyModule ? this.props?.forwardedTags : {},
|
|
1382
|
+
skipTags: this.skipTags.bind(this)
|
|
1387
1383
|
});
|
|
1388
1384
|
} else {
|
|
1389
1385
|
this.props.onSubmit(this.state.formData);
|
|
@@ -1410,6 +1406,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1410
1406
|
|
|
1411
1407
|
// Set up callbacks for error and success handling
|
|
1412
1408
|
const onLiquidError = ({ standardErrors, liquidErrors }) => {
|
|
1409
|
+
|
|
1413
1410
|
this.setState(
|
|
1414
1411
|
(prevState) => ({
|
|
1415
1412
|
liquidErrorMessage: {
|
|
@@ -1421,8 +1418,6 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1421
1418
|
() => {
|
|
1422
1419
|
this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage, this.state.currentTab);
|
|
1423
1420
|
this.props.stopValidation();
|
|
1424
|
-
// Block save: tell parent form is invalid so Done/submit is blocked
|
|
1425
|
-
this.props.onFormValidityChange(false, this.state.errorData);
|
|
1426
1421
|
}
|
|
1427
1422
|
);
|
|
1428
1423
|
};
|
|
@@ -1452,6 +1447,13 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1452
1447
|
getLiquidTags: this.props.actions.getLiquidTags,
|
|
1453
1448
|
formatMessage: this.props.intl.formatMessage,
|
|
1454
1449
|
messages: messages,
|
|
1450
|
+
tagLookupMap: this.props?.metaEntities?.tagLookupMap,
|
|
1451
|
+
eventContextTags: this.props?.eventContextTags,
|
|
1452
|
+
isLiquidFlow: this.liquidFlow(), // Use the method instead of props
|
|
1453
|
+
forwardedTags: this.props?.isLoyaltyModule ? this.props?.forwardedTags : {},
|
|
1454
|
+
skipTags: this.skipTags.bind(this),
|
|
1455
|
+
extractNames,
|
|
1456
|
+
checkSupport,
|
|
1455
1457
|
singleTab: singleTab?.toUpperCase(),
|
|
1456
1458
|
});
|
|
1457
1459
|
}
|
|
@@ -1506,41 +1508,116 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1506
1508
|
});
|
|
1507
1509
|
}
|
|
1508
1510
|
|
|
1509
|
-
validateTags(content, tagsParam, isEmail = false, isFullMode = this.props?.isFullMode) {
|
|
1511
|
+
validateTags(content, tagsParam, injectedTagsParams, isEmail = false, isFullMode = this.props?.isFullMode) {
|
|
1512
|
+
const type = (this.props.location && this.props.location.query.type) ? this.props.location.query.type : 'full';
|
|
1510
1513
|
let currentModule = this.props.location.query.module ? this.props.location.query.module : 'default';
|
|
1511
1514
|
if (this.props.tagModule) {
|
|
1512
1515
|
currentModule = this.props.tagModule;
|
|
1513
1516
|
}
|
|
1514
1517
|
const tags = tagsParam ? tagsParam : this.props.tags;
|
|
1515
|
-
const
|
|
1516
|
-
const
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1518
|
+
const injectedTags = this.transformInjectedTags(injectedTagsParams ? injectedTagsParams : this.props.injectedTags);
|
|
1519
|
+
const excludedTags = ['user_id_b64', 'outbox_id_b64'];
|
|
1520
|
+
|
|
1521
|
+
|
|
1522
|
+
const response = {};
|
|
1523
|
+
response.valid = true;
|
|
1524
|
+
response.missingTags = [];
|
|
1525
|
+
response.unsupportedTags = [];
|
|
1526
|
+
response.isBraceError = false;
|
|
1527
|
+
response.isContentEmpty = false;
|
|
1528
|
+
const contentForValidation = isEmail ? convert(content, GLOBAL_CONVERT_OPTIONS) : content ;
|
|
1529
|
+
const isModuleTypeOutbound = (this.props?.moduleType || '').toUpperCase() === OUTBOUND;
|
|
1530
|
+
// Run tag validation (missing + unsupported): library mode, or full mode with liquid support, or
|
|
1531
|
+
// legacy Email (CK Editor) when unsubscribe is required (EMAIL_UNSUBSCRIBE_TAG_MANDATORY false) so missing-unsubscribe error shows
|
|
1532
|
+
const shouldRunTagValidation = !isFullMode
|
|
1533
|
+
|| (isEmail && hasLiquidSupportFeature())
|
|
1534
|
+
|| (isEmail && !isEmailUnsubscribeTagMandatory() && isModuleTypeOutbound);
|
|
1535
|
+
if (tags && tags.length && !isFullMode) {
|
|
1536
|
+
_.forEach(tags, (tag) => {
|
|
1537
|
+
_.forEach(tag.definition.supportedModules, (module) => {
|
|
1538
|
+
if (module.mandatory && (currentModule === module.context)) {
|
|
1539
|
+
if (content.indexOf(`{{${tag.definition.value}}}`) === -1) {
|
|
1540
|
+
response.valid = false;
|
|
1541
|
+
response.missingTags.push(tag.definition.value);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
});
|
|
1545
|
+
});
|
|
1546
|
+
// Legacy Email (CK Editor): when unsubscribe is required, ensure we validate it even if tag schema didn't mark it mandatory for this module
|
|
1547
|
+
if (isEmail && !isEmailUnsubscribeTagMandatory() && isModuleTypeOutbound) {
|
|
1548
|
+
const hasUnsubscribeInContent = /{{unsubscribe(\(#[a-zA-Z\d]{6}\))?}}/g.test(content);
|
|
1549
|
+
if (!hasUnsubscribeInContent && response.missingTags.indexOf('unsubscribe') === -1) {
|
|
1550
|
+
response.valid = false;
|
|
1551
|
+
response.missingTags.push('unsubscribe');
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
const regex = /{{[(A-Z\w+(\s\w+)*$\(\)@!#$%^&*~.,/\\]+}}/g;
|
|
1555
|
+
let match = regex.exec(content);
|
|
1556
|
+
const regexImgSrc=/<img[^>]*\bsrc\s*=\s*"[^"]*{{customer_barcode}}[^"]*"/;
|
|
1557
|
+
let matchImg = regexImgSrc.exec(content);
|
|
1558
|
+
const regexCustomerBarcode = /{{customer_barcode}}(?![^<]*>)/g;
|
|
1559
|
+
let matchCustomerBarcode = regexCustomerBarcode.exec(content);
|
|
1560
|
+
// \S matches anything other than a space, a tab, a newline, or a carriage return.
|
|
1561
|
+
const validString= /\S/.test(contentForValidation);
|
|
1562
|
+
if (isEmailUnsubscribeTagMandatory() && isEmail && isModuleTypeOutbound) {
|
|
1563
|
+
const missingTagIndex = response?.missingTags?.indexOf("unsubscribe");
|
|
1564
|
+
if(missingTagIndex != -1) { //skip regex tags for mandatory tags also
|
|
1565
|
+
response?.missingTags?.splice(missingTagIndex, 1);
|
|
1566
|
+
}
|
|
1567
|
+
if (validString) {
|
|
1568
|
+
response.valid = true;
|
|
1569
|
+
} else {
|
|
1570
|
+
response.isContentEmpty = true;
|
|
1571
|
+
}
|
|
1539
1572
|
}
|
|
1540
|
-
|
|
1541
|
-
|
|
1573
|
+
while (match !== null ) {
|
|
1574
|
+
const tagValue = match[0].substring(this.indexOfEnd(match[0], '{{'), match[0].indexOf('}}'));
|
|
1575
|
+
const tagIndex = match?.index;
|
|
1576
|
+
match = regex.exec(content);
|
|
1577
|
+
let ifSupported = false;
|
|
1578
|
+
_.forEach(tags, (tag) => {
|
|
1579
|
+
if (tag.definition.value === tagValue) {
|
|
1580
|
+
ifSupported = true;
|
|
1581
|
+
}
|
|
1582
|
+
if(tagValue === CUSTOMER_BARCODE_TAG && (matchImg === null || matchCustomerBarcode !== null)){
|
|
1583
|
+
ifSupported = false;
|
|
1584
|
+
}
|
|
1585
|
+
});
|
|
1586
|
+
const ifSkipped = this.skipTags(tagValue);
|
|
1587
|
+
if (ifSkipped) {
|
|
1588
|
+
ifSupported = true;
|
|
1589
|
+
let isUnsubscribeSkipped = tagValue.indexOf("unsubscribe") != -1 ;
|
|
1590
|
+
if (isUnsubscribeSkipped) {
|
|
1591
|
+
const missingTagIndex = response.missingTags.indexOf("unsubscribe");
|
|
1592
|
+
if(missingTagIndex != -1) { //skip regex tags for mandatory tags also
|
|
1593
|
+
response.missingTags.splice(missingTagIndex, 1);
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
// Event Context Tags support
|
|
1599
|
+
this.props?.eventContextTags?.forEach((tag) => {
|
|
1600
|
+
if (tagValue === tag?.tagName) {
|
|
1601
|
+
ifSupported = true;
|
|
1602
|
+
}
|
|
1603
|
+
});
|
|
1604
|
+
|
|
1605
|
+
ifSupported = ifSupported || this.checkIfSupportedTag(tagValue, injectedTags);
|
|
1606
|
+
// Only add to unsupportedTags if not inside a {% ... %} block (scenario 3: liquid orgs also get unsupported-tag errors)
|
|
1607
|
+
if (!ifSupported && !isInsideLiquidBlock(content, tagIndex)) {
|
|
1608
|
+
response.unsupportedTags.push(tagValue);
|
|
1609
|
+
response.valid = false;
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
if (response?.unsupportedTags?.length == 0 && response?.missingTags?.length == 0 ) {
|
|
1613
|
+
response.valid = true;
|
|
1614
|
+
}
|
|
1542
1615
|
}
|
|
1543
1616
|
}
|
|
1617
|
+
if(!validateIfTagClosed(contentForValidation)){
|
|
1618
|
+
response.isBraceError = true;
|
|
1619
|
+
response.valid = false;
|
|
1620
|
+
}
|
|
1544
1621
|
return response;
|
|
1545
1622
|
}
|
|
1546
1623
|
/* eslint-enable */
|
|
@@ -2773,21 +2850,21 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
2773
2850
|
|
|
2774
2851
|
|
|
2775
2852
|
getMissingOrUnsupportedTagsName = (content = '', type) => {
|
|
2776
|
-
const { MISSING_TAGS } = tagsTypes;
|
|
2853
|
+
const { MISSING_TAGS, UNSUPPORTED_TAGS} = tagsTypes;
|
|
2777
2854
|
const tagValidationResponse = this.validateTags(content);
|
|
2778
|
-
if (type && type === MISSING_TAGS) {
|
|
2779
|
-
return
|
|
2855
|
+
if (type && (type === MISSING_TAGS || type === UNSUPPORTED_TAGS)) {
|
|
2856
|
+
return tagValidationResponse[type].join(', ').toString();
|
|
2780
2857
|
}
|
|
2781
2858
|
return null;
|
|
2782
2859
|
};
|
|
2783
2860
|
|
|
2784
2861
|
renderTextAreaContent = (styling, columns, val, isVersionEnable, rows, cols, offset = 0) => {
|
|
2785
2862
|
const { checkValidation, errorData, currentTab, formData } = this.state;
|
|
2786
|
-
const { MISSING_TAGS } = tagsTypes;
|
|
2863
|
+
const { MISSING_TAGS, UNSUPPORTED_TAGS } = tagsTypes;
|
|
2787
2864
|
const errorType = (isVersionEnable ? errorData[`${currentTab - 1}`][val.id] : errorData[val.id]);
|
|
2788
2865
|
const ifError = checkValidation && errorType;
|
|
2789
2866
|
const messageContent = isVersionEnable ? formData[`${currentTab - 1}`][val.id] : formData[val.id];
|
|
2790
|
-
const { MISSING_TAG_ERROR, TAG_BRACKET_COUNT_MISMATCH_ERROR } = errorMessageForTags;
|
|
2867
|
+
const { MISSING_TAG_ERROR, UNSUPPORTED_TAG_ERROR, TAG_BRACKET_COUNT_MISMATCH_ERROR } = errorMessageForTags;
|
|
2791
2868
|
const { formatMessage } = this.props.intl;
|
|
2792
2869
|
|
|
2793
2870
|
const { accessibleFeatures = [] } = this.props.currentOrgDetails || {};
|
|
@@ -2800,6 +2877,9 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
2800
2877
|
case MISSING_TAG_ERROR:
|
|
2801
2878
|
errorMessageText = formatMessage(messages.missingTagsValidationError, {missingTags: this.getMissingOrUnsupportedTagsName(messageContent, MISSING_TAGS)});
|
|
2802
2879
|
break;
|
|
2880
|
+
case UNSUPPORTED_TAG_ERROR:
|
|
2881
|
+
errorMessageText = formatMessage(messages.unsupportedTagsValidationError, {unsupportedTags: this.getMissingOrUnsupportedTagsName(messageContent, UNSUPPORTED_TAGS)});
|
|
2882
|
+
break;
|
|
2803
2883
|
case TAG_BRACKET_COUNT_MISMATCH_ERROR:
|
|
2804
2884
|
errorMessageText = formatMessage(globalMessages.unbalanacedCurlyBraces);
|
|
2805
2885
|
break;
|
|
@@ -2813,13 +2893,8 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
2813
2893
|
if (this.props.restrictPersonalization && hasPersonalizationTags(messageContent)) {
|
|
2814
2894
|
errorMessageText = formatMessage(messages.personalizationTagsErrorMessage);
|
|
2815
2895
|
}
|
|
2816
|
-
// Empty/required error: only show after user has triggered validation (ifError / "Done").
|
|
2817
|
-
// All other errors (brace, personalization, missing tags, generic): show in real time while typing.
|
|
2818
|
-
const isContentEmpty = !messageContent || !/\S/.test(String(messageContent).trim());
|
|
2819
|
-
const isEmptyError = errorType && isContentEmpty;
|
|
2820
|
-
const showError = errorType && (isEmptyError ? ifError : true);
|
|
2821
2896
|
const prevErrorMessage = this.state.liquidErrorMessage?.STANDARD_ERROR_MSG?.[0];
|
|
2822
|
-
if (prevErrorMessage !== errorMessageText && errorMessageText && this.
|
|
2897
|
+
if (prevErrorMessage !== errorMessageText && errorMessageText && this.liquidFlow()) {
|
|
2823
2898
|
this.setState((prevState) => ({
|
|
2824
2899
|
liquidErrorMessage: {
|
|
2825
2900
|
...prevState.liquidErrorMessage,
|
|
@@ -2830,25 +2905,15 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
2830
2905
|
this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage, this.props.channel === SMS? null: this.state.currentTab);
|
|
2831
2906
|
});
|
|
2832
2907
|
}
|
|
2833
|
-
|
|
2834
|
-
// Previously the textarea was inside the `else` branch, so it was not rendered during the
|
|
2835
|
-
// one render cycle when the liquid error message was first set – causing the input to lose
|
|
2836
|
-
// focus whenever a brace/tag error first appeared.
|
|
2908
|
+
else{
|
|
2837
2909
|
if (styling === 'semantic') {
|
|
2838
2910
|
columns.push(
|
|
2839
2911
|
<CapColumn key="input" span={val.width} offset={offset}>
|
|
2840
2912
|
<TextArea
|
|
2841
2913
|
id={val.id}
|
|
2842
2914
|
placeholder={val.placeholder ? val.placeholder : ''}
|
|
2843
|
-
className={`${
|
|
2844
|
-
errorMessage={
|
|
2845
|
-
showError && errorMessageText && (
|
|
2846
|
-
!this.isLiquidFlowSupportedByChannel() ||
|
|
2847
|
-
[MOBILE_PUSH, INAPP].includes(this.props.schema?.channel?.toUpperCase())
|
|
2848
|
-
)
|
|
2849
|
-
? errorMessageText
|
|
2850
|
-
: ''
|
|
2851
|
-
}
|
|
2915
|
+
className={`${ifError ? 'error-form-builder' : ''}`}
|
|
2916
|
+
errorMessage={errorMessageText && !this.liquidFlow() ? errorMessageText : ''}
|
|
2852
2917
|
label={val.label}
|
|
2853
2918
|
autosize={val.autosize ? val.autosizeParams : false}
|
|
2854
2919
|
onChange={(e) => this.updateFormData(e.target.value, val)}
|
|
@@ -2879,6 +2944,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
2879
2944
|
</CapColumn>
|
|
2880
2945
|
);
|
|
2881
2946
|
}
|
|
2947
|
+
}
|
|
2882
2948
|
};
|
|
2883
2949
|
|
|
2884
2950
|
|
|
@@ -3941,7 +4007,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3941
4007
|
<CapColumn
|
|
3942
4008
|
style={val.colStyle ? val.colStyle : {border : ""}}
|
|
3943
4009
|
span={val.width}
|
|
3944
|
-
className={(this.state.liquidErrorMessage?.LIQUID_ERROR_MSG?.length || this.state.liquidErrorMessage?.STANDARD_ERROR_MSG?.length) && this.
|
|
4010
|
+
className={`${(this.state.liquidErrorMessage?.LIQUID_ERROR_MSG?.length || this.state.liquidErrorMessage?.STANDARD_ERROR_MSG?.length) && this.liquidFlow() && "error-boundary"} `}
|
|
3945
4011
|
>
|
|
3946
4012
|
<CKEditor
|
|
3947
4013
|
id={val.id}
|
|
@@ -3985,7 +4051,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3985
4051
|
isModuleFilterEnabled = this.props.isFullMode;
|
|
3986
4052
|
}
|
|
3987
4053
|
columns.push(
|
|
3988
|
-
<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.
|
|
4054
|
+
<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.liquidFlow() && "error-boundary"}`}>
|
|
3989
4055
|
<BeeEditor
|
|
3990
4056
|
uid={uuid}
|
|
3991
4057
|
tokenData={beeToken}
|
|
@@ -4271,7 +4337,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
4271
4337
|
|
|
4272
4338
|
|
|
4273
4339
|
return (
|
|
4274
|
-
<CapSpin spinning={Boolean((this.
|
|
4340
|
+
<CapSpin spinning={Boolean((this.liquidFlow() && this.props.liquidExtractionInProgress) || this.props.metaDataStatus === REQUEST)} tip={this.props.intl.formatMessage(messages.liquidSpinText)} >
|
|
4275
4341
|
<CapRow>
|
|
4276
4342
|
{this.props.schema && this.renderForm()}
|
|
4277
4343
|
<SlideBox
|
|
@@ -26,6 +26,10 @@ 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
|
+
},
|
|
29
33
|
genericTagsValidationError: {
|
|
30
34
|
id: 'creatives.components.FormBuilder.genericTagsValidationError',
|
|
31
35
|
defaultMessage: 'Please check the message content for unsupported/missing tags',
|
|
@@ -38,6 +42,10 @@ export default defineMessages({
|
|
|
38
42
|
id: 'creatives.componentsV2.FormBuilder.missingTags',
|
|
39
43
|
defaultMessage: 'Missing tags are:',
|
|
40
44
|
},
|
|
45
|
+
unsupportedTags: {
|
|
46
|
+
id: 'creatives.componentsV2.FormBuilder.unsupportedTags',
|
|
47
|
+
defaultMessage: 'Unsupported tags are:',
|
|
48
|
+
},
|
|
41
49
|
upload: {
|
|
42
50
|
id: 'creatives.componentsV2.FormBuilder.upload',
|
|
43
51
|
defaultMessage: 'Upload',
|
|
@@ -102,6 +102,7 @@ const HTMLEditor = forwardRef(({
|
|
|
102
102
|
onTagSelect = null,
|
|
103
103
|
onContextChange = null,
|
|
104
104
|
globalActions = null,
|
|
105
|
+
isLiquidEnabled = false, // Controls Liquid tab visibility in ValidationTabs
|
|
105
106
|
isFullMode = true, // Full mode vs library mode - controls layout and visibility
|
|
106
107
|
onErrorAcknowledged = null, // Callback when user clicks redirection icon to acknowledge errors
|
|
107
108
|
onValidationChange = null, // Callback when validation state changes (for parent to track errors)
|
|
@@ -573,6 +574,7 @@ const HTMLEditor = forwardRef(({
|
|
|
573
574
|
content,
|
|
574
575
|
layout,
|
|
575
576
|
validation,
|
|
577
|
+
isLiquidEnabled,
|
|
576
578
|
editorRef: getActiveEditorRef(),
|
|
577
579
|
handleLabelInsert,
|
|
578
580
|
handleSave,
|
|
@@ -592,6 +594,7 @@ const HTMLEditor = forwardRef(({
|
|
|
592
594
|
content,
|
|
593
595
|
layout,
|
|
594
596
|
validation,
|
|
597
|
+
isLiquidEnabled,
|
|
595
598
|
getActiveEditorRef,
|
|
596
599
|
handleLabelInsert,
|
|
597
600
|
handleSave,
|
|
@@ -780,6 +783,7 @@ HTMLEditor.propTypes = {
|
|
|
780
783
|
onTagSelect: PropTypes.func,
|
|
781
784
|
onContextChange: PropTypes.func, // Deprecated: use globalActions instead
|
|
782
785
|
globalActions: PropTypes.object,
|
|
786
|
+
isLiquidEnabled: PropTypes.bool, // Controls Liquid tab visibility in validation
|
|
783
787
|
isFullMode: PropTypes.bool, // Full mode vs library mode
|
|
784
788
|
onErrorAcknowledged: PropTypes.func, // Callback when user clicks redirection icon to acknowledge errors
|
|
785
789
|
onValidationChange: PropTypes.func, // Callback when validation state changes
|
|
@@ -813,6 +817,7 @@ HTMLEditor.defaultProps = {
|
|
|
813
817
|
onTagSelect: null,
|
|
814
818
|
onContextChange: null,
|
|
815
819
|
globalActions: null, // Redux actions for API calls
|
|
820
|
+
isLiquidEnabled: false,
|
|
816
821
|
isFullMode: true, // Default to full mode
|
|
817
822
|
onErrorAcknowledged: null, // Callback when user clicks redirection icon to acknowledge errors
|
|
818
823
|
onValidationChange: null, // Callback when validation state changes
|