@capillarytech/creatives-library 8.0.10 → 8.0.12
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/config/app.js +2 -0
- package/containers/App/constants.js +1 -0
- package/initialState.js +3 -0
- package/package.json +1 -1
- package/services/api.js +23 -2
- package/services/tests/api.test.js +181 -0
- package/utils/common.js +7 -0
- package/utils/commonUtils.js +7 -1
- package/utils/tagValidations.js +99 -1
- package/utils/tests/commonUtil.test.js +37 -1
- package/utils/tests/tagValidations.test.js +392 -2
- package/v2Components/Ckeditor/index.js +12 -10
- package/v2Components/ErrorInfoNote/index.js +89 -0
- package/v2Components/ErrorInfoNote/messages.js +29 -0
- package/v2Components/ErrorInfoNote/style.scss +72 -0
- package/v2Components/FormBuilder/_formBuilder.scss +4 -1
- package/v2Components/FormBuilder/index.js +172 -74
- package/v2Components/FormBuilder/messages.js +8 -0
- package/v2Containers/Cap/actions.js +8 -0
- package/v2Containers/Cap/constants.js +4 -0
- package/v2Containers/Cap/mockData.js +74 -0
- package/v2Containers/Cap/reducer.js +12 -0
- package/v2Containers/Cap/sagas.js +28 -1
- package/v2Containers/Cap/selectors.js +5 -0
- package/v2Containers/Cap/tests/__snapshots__/index.test.js.snap +1 -0
- package/v2Containers/Cap/tests/reducer.test.js +50 -0
- package/v2Containers/Cap/tests/saga.test.js +81 -1
- package/v2Containers/CreativesContainer/SlideBoxContent.js +7 -0
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +40 -17
- package/v2Containers/CreativesContainer/constants.js +1 -0
- package/v2Containers/CreativesContainer/index.js +45 -4
- package/v2Containers/CreativesContainer/index.scss +13 -1
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +16 -0
- package/v2Containers/Email/_email.scss +3 -0
- package/v2Containers/Email/index.js +2 -0
- package/v2Containers/EmailWrapper/index.js +3 -0
- package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +3 -0
- package/v2Containers/MobilePush/Edit/constants.js +2 -0
- package/v2Containers/MobilePush/Edit/index.js +124 -30
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +93 -0
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +12 -0
- package/v2Containers/Viber/constants.js +1 -1
- package/v2Containers/Viber/index.js +19 -19
- package/v2Containers/Viber/messages.js +0 -4
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +12 -0
|
@@ -44,19 +44,22 @@ import EDMEditor from "../Edmeditor";
|
|
|
44
44
|
import BeeEditor from '../../v2Containers/BeeEditor';
|
|
45
45
|
import CustomPopOver from '../CustomPopOver';
|
|
46
46
|
import messages from './messages';
|
|
47
|
-
import { selectCurrentOrgDetails } from "../../v2Containers/Cap/selectors";
|
|
47
|
+
import { makeSelectMetaEntities, selectCurrentOrgDetails, selectLiquidStateDetails } from "../../v2Containers/Cap/selectors";
|
|
48
|
+
import * as actions from "../../v2Containers/Cap/actions";
|
|
48
49
|
import './_formBuilder.scss';
|
|
49
50
|
import {updateCharCount, checkUnicode} from "../../utils/smsCharCountV2";
|
|
50
|
-
import {
|
|
51
|
-
import {
|
|
51
|
+
import { checkSupport, extractNames, preprocessHtml, validateIfTagClosed } from '../../utils/tagValidations';
|
|
52
|
+
import { SMS, MOBILE_PUSH, LINE, ENABLE_AI_SUGGESTIONS,AI_CONTENT_BOT_DISABLED, EMAIL, LIQUID_SUPPORTED_CHANNELS } from '../../v2Containers/CreativesContainer/constants';
|
|
52
53
|
import globalMessages from '../../v2Containers/Cap/messages';
|
|
53
54
|
import { convert } from 'html-to-text';
|
|
54
55
|
import { OUTBOUND } from './constants';
|
|
55
56
|
import { GET_TRANSLATION_MAPPED } from '../../containers/TagList/constants';
|
|
56
57
|
import moment from 'moment';
|
|
57
58
|
import { CUSTOMER_BARCODE_TAG , COPY_OF} from '../../containers/App/constants';
|
|
58
|
-
import { isEmailUnsubscribeTagMandatory } from '../../utils/common';
|
|
59
|
+
import { hasLiquidSupportFeature, isEmailUnsubscribeTagMandatory } from '../../utils/common';
|
|
59
60
|
import { isUrl } from '../../v2Containers/Line/Container/Wrapper/utils';
|
|
61
|
+
import { bindActionCreators } from 'redux';
|
|
62
|
+
|
|
60
63
|
const TabPane = Tabs.TabPane;
|
|
61
64
|
const {Column} = Table;
|
|
62
65
|
const {TextArea} = CapInput;
|
|
@@ -95,6 +98,10 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
95
98
|
customPopoverVisible: false,
|
|
96
99
|
isDrawerVisible: false,
|
|
97
100
|
translationLang: 'en',
|
|
101
|
+
liquidErrorMessage: {
|
|
102
|
+
STANDARD_ERROR_MSG: [],
|
|
103
|
+
LIQUID_ERROR_MSG: [],
|
|
104
|
+
},
|
|
98
105
|
};
|
|
99
106
|
this.renderForm = this.renderForm.bind(this);
|
|
100
107
|
this.updateFormData = this.updateFormData.bind(this);
|
|
@@ -126,6 +133,8 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
126
133
|
this.transformInjectedTags = this.transformInjectedTags.bind(this);
|
|
127
134
|
this.handleSetRadioValue = this.handleSetRadioValue.bind(this);
|
|
128
135
|
this.formElements = [];
|
|
136
|
+
// Check if the liquid flow feature is supported and the channel is in the supported list.
|
|
137
|
+
this.liquidFlow = hasLiquidSupportFeature() && LIQUID_SUPPORTED_CHANNELS.includes(props?.schema?.channel?.toUpperCase());
|
|
129
138
|
}
|
|
130
139
|
|
|
131
140
|
componentWillMount() {
|
|
@@ -480,10 +489,10 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
480
489
|
validateForm(tags, injectedTags, isSave, showMessages, onValidationComplete) {
|
|
481
490
|
const channel = _.get(this, "props.schema.channel");
|
|
482
491
|
let isValid = true;
|
|
492
|
+
let isLiquidValid = true;
|
|
483
493
|
const type = (this.props.location && this.props.location.query.type) ? this.props.location.query.type : 'full';
|
|
484
494
|
const currentModule = (this.props.location && this.props.location.query.module) ? this.props.location.query.module : 'default';
|
|
485
495
|
let errorData = _.cloneDeep(this.state.errorData);
|
|
486
|
-
const stateFormData = _.cloneDeep(this.state.formData);
|
|
487
496
|
if (channel && channel.toUpperCase() === SMS) {
|
|
488
497
|
for (let count = 0; count < this.state.tabCount; count += 1) {
|
|
489
498
|
if (_.isEmpty(errorData[count])) {
|
|
@@ -915,10 +924,10 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
915
924
|
}
|
|
916
925
|
|
|
917
926
|
// Form Data Validation for Email Channel
|
|
918
|
-
if (this.props
|
|
927
|
+
if (this.props?.schema?.channel?.toUpperCase() === EMAIL) {
|
|
919
928
|
// Validating Email By iterating all elements of FormBuilder
|
|
920
929
|
const isEmail = true;
|
|
921
|
-
_.forEach(this.state.formData,
|
|
930
|
+
_.forEach(this.state.formData,(data, index) => {
|
|
922
931
|
|
|
923
932
|
if (!this.state.formData["template-subject"] && type.toLowerCase() === 'embedded' && (currentModule.toLowerCase() === 'loyalty' || currentModule.toLowerCase() === 'dvs' || currentModule.toLowerCase() === 'timeline' || currentModule.toLowerCase() === 'library')) {
|
|
924
933
|
errorData["template-subject"] = true;
|
|
@@ -950,7 +959,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
950
959
|
} else if(index === 'template-version' && data !== '') {
|
|
951
960
|
errorData[index] = false;
|
|
952
961
|
}
|
|
953
|
-
|
|
962
|
+
isLiquidValid = isValid; // Existing validations support for liquid enabled orgs
|
|
954
963
|
// Check error for the versions
|
|
955
964
|
if (!isNaN(index) || index === 'base') {
|
|
956
965
|
if (errorData[index] === undefined) {
|
|
@@ -963,37 +972,57 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
963
972
|
if (!content) {
|
|
964
973
|
return false;
|
|
965
974
|
}
|
|
966
|
-
const tagValidationResponse = this.validateTags(content, tags, injectedTags, isEmail)
|
|
975
|
+
const tagValidationResponse = this.validateTags(content, tags, injectedTags, isEmail);
|
|
967
976
|
if (errorData[index][currentLang] === undefined) {
|
|
968
977
|
errorData[index][currentLang] = {};
|
|
969
978
|
}
|
|
970
|
-
|
|
971
|
-
if (tagValidationResponse.valid) {
|
|
979
|
+
if (tagValidationResponse?.valid) {
|
|
972
980
|
errorData[index][currentLang]['template-content'] = false;
|
|
973
981
|
} else {
|
|
974
982
|
errorData[index][currentLang]['template-content'] = true;
|
|
975
983
|
isValid = false;
|
|
976
|
-
|
|
977
|
-
|
|
984
|
+
isLiquidValid = false;
|
|
985
|
+
if ((showMessages && !isNaN(index)) || this.liquidFlow) {
|
|
986
|
+
if (tagValidationResponse?.missingTags?.length > 0 || tagValidationResponse?.unsupportedTags?.length > 0) {
|
|
978
987
|
errorString += `${this.props.intl.formatMessage(messages.contentNotValidLanguage)} ${currentLang}\n`;
|
|
979
988
|
}
|
|
980
|
-
if (tagValidationResponse
|
|
989
|
+
if (tagValidationResponse?.missingTags?.length > 0) {
|
|
981
990
|
errorString += `${this.props.intl.formatMessage(messages.missingTags)} ${tagValidationResponse.missingTags.toString()}\n`;
|
|
982
991
|
}
|
|
983
|
-
if (tagValidationResponse
|
|
992
|
+
if (tagValidationResponse?.unsupportedTags?.length > 0) {
|
|
984
993
|
errorString += `${this.props.intl.formatMessage(messages.unsupportedTags)} ${tagValidationResponse.unsupportedTags.toString()}\n`;
|
|
985
994
|
}
|
|
986
|
-
if (tagValidationResponse
|
|
995
|
+
if (tagValidationResponse?.isBraceError){
|
|
987
996
|
errorString += this.props.intl.formatMessage(globalMessages.unbalanacedCurlyBraces);
|
|
988
997
|
}
|
|
989
998
|
if (tagValidationResponse?.isContentEmpty) {
|
|
990
999
|
errorString += this.props.intl.formatMessage(messages.emailBodyEmptyError);
|
|
1000
|
+
// Adds a bypass for cases where content is initially empty in the creation flow.
|
|
1001
|
+
if(this.liquidFlow && !this.props.isEdit){
|
|
1002
|
+
errorString = "";
|
|
1003
|
+
isLiquidValid = true;
|
|
1004
|
+
}
|
|
991
1005
|
}
|
|
992
1006
|
}
|
|
993
1007
|
}
|
|
994
1008
|
}
|
|
995
1009
|
if (errorString !== "") {
|
|
996
|
-
this.
|
|
1010
|
+
if (this.liquidFlow) {
|
|
1011
|
+
this.setState(
|
|
1012
|
+
(prevState) => ({
|
|
1013
|
+
liquidErrorMessage: {
|
|
1014
|
+
...prevState.liquidErrorMessage,
|
|
1015
|
+
STANDARD_ERROR_MSG: [errorString],
|
|
1016
|
+
},
|
|
1017
|
+
}),
|
|
1018
|
+
() => {
|
|
1019
|
+
// Callback after the state is updated
|
|
1020
|
+
this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage);
|
|
1021
|
+
}
|
|
1022
|
+
);
|
|
1023
|
+
} else {
|
|
1024
|
+
this.openNotificationWithIcon('error', errorString, "email-validation-error");
|
|
1025
|
+
}
|
|
997
1026
|
}
|
|
998
1027
|
_.forEach(errorData[index], (versionData, key) => {
|
|
999
1028
|
if (data.selectedLanguages.indexOf(key) === -1) {
|
|
@@ -1002,39 +1031,28 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1002
1031
|
});
|
|
1003
1032
|
}
|
|
1004
1033
|
});
|
|
1005
|
-
// for (let count = 0; count < this.state.tabCount; count += 1) {
|
|
1006
|
-
// const index = count + 1;
|
|
1007
|
-
// // const content = this.state.formData[count][`template-content${index > 1 ? index : ''}`];
|
|
1008
|
-
// // const tagValidationResponse = this.validateTags(content, tags, injectedTags);
|
|
1009
|
-
// //
|
|
1010
|
-
// // if (tagValidationResponse.valid) {
|
|
1011
|
-
// // errorData[count][`template-content${index > 1 ? index : ''}`] = false;
|
|
1012
|
-
// // } else {
|
|
1013
|
-
// // errorData[count][`template-content${index > 1 ? index : ''}`] = true;
|
|
1014
|
-
// // isValid = false;
|
|
1015
|
-
// // }
|
|
1016
|
-
// }
|
|
1017
|
-
// if (type.toLowerCase() === 'embedded' && (currentModule.toLowerCase() === 'loyalty' || currentModule.toLowerCase() === 'dvs' || currentModule.toLowerCase() === 'timeline')) {
|
|
1018
|
-
// if (this.state.formData['template-subject'] && this.state.formData['template-subject'].trim() === '') {
|
|
1019
|
-
// errorData['template-subject'] = true;
|
|
1020
|
-
// isValid = false;
|
|
1021
|
-
// }
|
|
1022
|
-
// }
|
|
1023
|
-
// if(type !== 'embedded' && (!this.state.formData['template-name'] || this.state.formData['template-name'] === '')) {
|
|
1024
|
-
// errorData['template-name'] = true;
|
|
1025
|
-
// isValid = false;
|
|
1026
|
-
// } else {
|
|
1027
|
-
// errorData['template-name'] = false;
|
|
1028
|
-
// }
|
|
1029
1034
|
}
|
|
1030
|
-
|
|
1035
|
+
|
|
1036
|
+
const isTemplateValid = this.liquidFlow ? isLiquidValid : isValid;
|
|
1037
|
+
//Updating the state with the error data
|
|
1038
|
+
this.setState((prevState) => ({
|
|
1039
|
+
isFormValid: isTemplateValid,
|
|
1040
|
+
liquidErrorMessage: {
|
|
1041
|
+
//Do not update the LIQUID_ERROR_MSG validation error message
|
|
1042
|
+
...prevState.liquidErrorMessage,
|
|
1043
|
+
//update Standard error message based on the updated content
|
|
1044
|
+
STANDARD_ERROR_MSG: isTemplateValid ? [] : prevState.liquidErrorMessage?.STANDARD_ERROR_MSG,
|
|
1045
|
+
},
|
|
1046
|
+
errorData,
|
|
1047
|
+
}), () => {
|
|
1048
|
+
this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage);
|
|
1031
1049
|
if (onValidationComplete) {
|
|
1032
1050
|
onValidationComplete();
|
|
1033
1051
|
}
|
|
1034
1052
|
});
|
|
1035
1053
|
|
|
1036
|
-
this.props.onFormValidityChange(
|
|
1037
|
-
return
|
|
1054
|
+
this.props.onFormValidityChange(isTemplateValid, errorData);
|
|
1055
|
+
return isTemplateValid;
|
|
1038
1056
|
}
|
|
1039
1057
|
|
|
1040
1058
|
indexOfEnd(targetString, string) {
|
|
@@ -1049,8 +1067,70 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1049
1067
|
this.props.getValidationData();
|
|
1050
1068
|
return;
|
|
1051
1069
|
}
|
|
1052
|
-
if (this.state.isFormValid) {
|
|
1053
|
-
|
|
1070
|
+
if ( this.state.isFormValid ) {
|
|
1071
|
+
if (this.liquidFlow) {
|
|
1072
|
+
//Converts given HTML content to plain text string.
|
|
1073
|
+
const content = convert(
|
|
1074
|
+
this.state.formData?.base?.en?.["template-content"]
|
|
1075
|
+
);
|
|
1076
|
+
/*
|
|
1077
|
+
The `handleResult` function is used as a callback for `getLiquidTags` to handle the results post-processing.
|
|
1078
|
+
It checks for errors and unsupported tags in the fetched liquid tags, displays any necessary validation messages,
|
|
1079
|
+
and proceeds with form submission if all checks pass.
|
|
1080
|
+
*/
|
|
1081
|
+
const handleResult = (result) => {
|
|
1082
|
+
const {formatMessage} = this.props.intl;
|
|
1083
|
+
// Checks for errors in the result and displays them if any are found.
|
|
1084
|
+
if (result?.errors?.length > 0) {
|
|
1085
|
+
this.setState((prevState) => ({
|
|
1086
|
+
liquidErrorMessage: {
|
|
1087
|
+
...prevState.liquidErrorMessage,
|
|
1088
|
+
LIQUID_ERROR_MSG: result?.errors?.map(error => error?.message) ?? [formatMessage(messages.somethingWentWrong)],
|
|
1089
|
+
},
|
|
1090
|
+
}) , () => {
|
|
1091
|
+
this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage);
|
|
1092
|
+
});
|
|
1093
|
+
this.props.stopValidation();
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
// Extracts the supported liquid tags from the given content and do the necessary checks.
|
|
1097
|
+
else {
|
|
1098
|
+
const extractedLiquidTags = extractNames(result?.data || []);
|
|
1099
|
+
// Extracts the supported liquid tags from the given content.
|
|
1100
|
+
const supportedLiquidTags = checkSupport(
|
|
1101
|
+
result,
|
|
1102
|
+
this.props?.metaEntities?.tagLookupMap
|
|
1103
|
+
);
|
|
1104
|
+
const unsupportedLiquidTags = extractedLiquidTags?.filter(
|
|
1105
|
+
(tag) => !supportedLiquidTags?.includes(tag) && !this.skipTags(tag)
|
|
1106
|
+
);
|
|
1107
|
+
if (
|
|
1108
|
+
unsupportedLiquidTags?.length > 0
|
|
1109
|
+
) {
|
|
1110
|
+
this.setState(
|
|
1111
|
+
(prevState) => ({
|
|
1112
|
+
liquidErrorMessage: {
|
|
1113
|
+
...prevState.liquidErrorMessage,
|
|
1114
|
+
LIQUID_ERROR_MSG: [formatMessage(messages.unsupportedTagsValidationError, {
|
|
1115
|
+
unsupportedTags: unsupportedLiquidTags.join(", ")})],
|
|
1116
|
+
},
|
|
1117
|
+
}),
|
|
1118
|
+
() => {
|
|
1119
|
+
this.props.showLiquidErrorInFooter(
|
|
1120
|
+
this.state.liquidErrorMessage
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
1123
|
+
);
|
|
1124
|
+
this.props.stopValidation();
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
1127
|
+
this.props.onSubmit(this.state.formData);
|
|
1128
|
+
}
|
|
1129
|
+
};
|
|
1130
|
+
this.props.actions.getLiquidTags(preprocessHtml(content), handleResult);
|
|
1131
|
+
} else {
|
|
1132
|
+
this.props.onSubmit(this.state.formData);
|
|
1133
|
+
}
|
|
1054
1134
|
} else {
|
|
1055
1135
|
this.setState({checkValidation: true});
|
|
1056
1136
|
this.validateForm(null, null, true);
|
|
@@ -1092,7 +1172,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1092
1172
|
});
|
|
1093
1173
|
|
|
1094
1174
|
return result;
|
|
1095
|
-
}
|
|
1175
|
+
};
|
|
1096
1176
|
|
|
1097
1177
|
skipTags(tag) {
|
|
1098
1178
|
const regexGroups = ["dynamic_expiry_date_after_\\d+_days.FORMAT_\\d", "unsubscribe\\(#[a-zA-Z\\d]{6}\\)","Link_to_[a-zA-z]","SURVEY.*.TOKEN","^[A-Za-z].*\\([a-zA-Z\\d]*\\)"];
|
|
@@ -1151,12 +1231,12 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1151
1231
|
if (isEmailUnsubscribeTagMandatory() && isEmail && this.props?.moduleType === OUTBOUND) {
|
|
1152
1232
|
const missingTagIndex = response?.missingTags?.indexOf("unsubscribe");
|
|
1153
1233
|
if(missingTagIndex != -1) { //skip regex tags for mandatory tags also
|
|
1154
|
-
response?.missingTags?.splice(missingTagIndex, 1);
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1234
|
+
response?.missingTags?.splice(missingTagIndex, 1);
|
|
1235
|
+
}
|
|
1236
|
+
if (validString) {
|
|
1237
|
+
response.valid = true;
|
|
1238
|
+
} else {
|
|
1239
|
+
response.isContentEmpty = true;
|
|
1160
1240
|
}
|
|
1161
1241
|
}
|
|
1162
1242
|
while (match !== null ) {
|
|
@@ -1183,11 +1263,12 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1183
1263
|
}
|
|
1184
1264
|
}
|
|
1185
1265
|
ifSupported = ifSupported || this.checkIfSupportedTag(tagValue, injectedTags);
|
|
1186
|
-
if (!ifSupported) {
|
|
1266
|
+
if (!ifSupported && !this.liquidFlow) {
|
|
1187
1267
|
response.unsupportedTags.push(tagValue);
|
|
1188
1268
|
response.valid = false;
|
|
1189
1269
|
}
|
|
1190
|
-
|
|
1270
|
+
|
|
1271
|
+
if (response?.unsupportedTags?.length == 0 && response?.missingTags?.length == 0 ) {
|
|
1191
1272
|
response.valid = true;
|
|
1192
1273
|
}
|
|
1193
1274
|
}
|
|
@@ -3338,7 +3419,11 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3338
3419
|
const currentLang = ((!_.isEmpty(this.state.formData) && !_.isEmpty(this.state.formData[`${this.state.currentTab - 1}`]) && !_.isEmpty(this.state.formData[`${this.state.currentTab - 1}`].selectedLanguages) && this.state.formData[`${this.state.currentTab - 1}`].selectedLanguages[langIndex]) ? this.state.formData[`${this.state.currentTab - 1}`].selectedLanguages[langIndex] : this.props.baseLanguage) || 'en';
|
|
3339
3420
|
const content = (!_.isEmpty(this.state.formData) && this.state.formData[`${this.state.currentTab - 1}`] && this.state.formData[`${this.state.currentTab - 1}`][currentLang] && this.state.formData[`${this.state.currentTab - 1}`][currentLang]['template-content'] || '');
|
|
3340
3421
|
columns.push(
|
|
3341
|
-
<CapColumn
|
|
3422
|
+
<CapColumn
|
|
3423
|
+
style={val.colStyle ? val.colStyle : {border : ""}}
|
|
3424
|
+
span={val.width}
|
|
3425
|
+
className={`${(this.state.liquidErrorMessage?.LIQUID_ERROR_MSG?.length || this.state.liquidErrorMessage?.STANDARD_ERROR_MSG?.length) && this.liquidFlow && "error-boundary"} `}
|
|
3426
|
+
>
|
|
3342
3427
|
<CKEditor
|
|
3343
3428
|
id={val.id}
|
|
3344
3429
|
content={content}
|
|
@@ -3381,7 +3466,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3381
3466
|
isModuleFilterEnabled = this.props.isFullMode;
|
|
3382
3467
|
}
|
|
3383
3468
|
columns.push(
|
|
3384
|
-
<CapColumn style={val.colStyle ? val.colStyle : {}} span={val.width}>
|
|
3469
|
+
<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"}`}>
|
|
3385
3470
|
<BeeEditor
|
|
3386
3471
|
uid={uuid}
|
|
3387
3472
|
tokenData={beeToken}
|
|
@@ -3485,7 +3570,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3485
3570
|
fields.push(row);
|
|
3486
3571
|
}
|
|
3487
3572
|
});
|
|
3488
|
-
if (!this.props.isEmailLoading && this.props.schema.channel.toUpperCase() ===
|
|
3573
|
+
if (!this.props.isEmailLoading && this.props.schema.channel.toUpperCase() === EMAIL && !fields.length) {
|
|
3489
3574
|
return (
|
|
3490
3575
|
<div style={{ textAlign: 'center' }}>
|
|
3491
3576
|
<CapSpin spinning />
|
|
@@ -3666,30 +3751,32 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3666
3751
|
|
|
3667
3752
|
|
|
3668
3753
|
return (
|
|
3754
|
+
<CapSpin spinning={this.liquidFlow && this.props.liquidExtractionInProgress} tip={this.props.intl.formatMessage(messages.liquidSpinText)} >
|
|
3669
3755
|
<CapRow>
|
|
3670
|
-
{
|
|
3671
|
-
this.props.schema ? this.renderForm() : ''
|
|
3672
|
-
}
|
|
3756
|
+
{this.props.schema && this.renderForm()}
|
|
3673
3757
|
<SlideBox
|
|
3674
|
-
header={
|
|
3758
|
+
header={
|
|
3759
|
+
<h3>{this.props.intl.formatMessage(messages.layoutSelection)}</h3>
|
|
3760
|
+
}
|
|
3675
3761
|
width={60}
|
|
3676
3762
|
content={cmsTemplateSelectionContent}
|
|
3677
3763
|
show={this.props.showEdmEmailTemplates}
|
|
3678
3764
|
handleClose={this.props.toggleEdmEmailTemplateSelection}
|
|
3679
|
-
loadingText={this.props.intl.formatMessage(
|
|
3765
|
+
loadingText={this.props.intl.formatMessage(
|
|
3766
|
+
messages.loadingEDMTemplates
|
|
3767
|
+
)}
|
|
3680
3768
|
isLoading={this.props.getCmsTemplatesInProgress}
|
|
3681
3769
|
/>
|
|
3682
|
-
{
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
)
|
|
3691
|
-
}
|
|
3770
|
+
{this.props.capDrawerContent && (
|
|
3771
|
+
<CapDrawer
|
|
3772
|
+
content={this.props.capDrawerContent}
|
|
3773
|
+
visible={this.state.isDrawerVisible}
|
|
3774
|
+
width={380}
|
|
3775
|
+
onClose={() => this.props.setDrawerVisibility(false)}
|
|
3776
|
+
/>
|
|
3777
|
+
)}
|
|
3692
3778
|
</CapRow>
|
|
3779
|
+
</CapSpin>
|
|
3693
3780
|
);
|
|
3694
3781
|
}
|
|
3695
3782
|
}
|
|
@@ -3697,6 +3784,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3697
3784
|
FormBuilder.defaultProps = {
|
|
3698
3785
|
isNewVersionFlow: false,
|
|
3699
3786
|
userLocale: localStorage.getItem('jlocale') || 'en',
|
|
3787
|
+
showLiquidErrorInFooter: () => {},
|
|
3700
3788
|
};
|
|
3701
3789
|
|
|
3702
3790
|
FormBuilder.propTypes = {
|
|
@@ -3743,10 +3831,20 @@ FormBuilder.propTypes = {
|
|
|
3743
3831
|
capDrawerContent: PropTypes.array,
|
|
3744
3832
|
isFullMode: PropTypes.bool,
|
|
3745
3833
|
currentOrgDetails: PropTypes.object,
|
|
3834
|
+
liquidExtractionInProgress: PropTypes.bool,
|
|
3835
|
+
showLiquidErrorInFooter: PropTypes.func,
|
|
3746
3836
|
};
|
|
3747
3837
|
|
|
3748
3838
|
const mapStateToProps = createStructuredSelector({
|
|
3749
3839
|
currentOrgDetails: selectCurrentOrgDetails(),
|
|
3840
|
+
liquidExtractionInProgress: selectLiquidStateDetails(),
|
|
3841
|
+
metaEntities: makeSelectMetaEntities(),
|
|
3750
3842
|
});
|
|
3751
3843
|
|
|
3752
|
-
|
|
3844
|
+
function mapDispatchToProps(dispatch) {
|
|
3845
|
+
return {
|
|
3846
|
+
actions: bindActionCreators(actions, dispatch),
|
|
3847
|
+
};
|
|
3848
|
+
}
|
|
3849
|
+
|
|
3850
|
+
export default connect(mapStateToProps,mapDispatchToProps)(injectIntl(FormBuilder));
|
|
@@ -90,4 +90,12 @@ export default defineMessages({
|
|
|
90
90
|
id: 'creatives.componentsV2.FormBuilder.emailBodyEmptyError',
|
|
91
91
|
defaultMessage: 'Email body cannot be empty',
|
|
92
92
|
},
|
|
93
|
+
somethingWentWrong: {
|
|
94
|
+
id: 'creatives.componentsV2.FormBuilder.somethingWentWrong',
|
|
95
|
+
defaultMessage: 'Something went wrong while validating content, please try again later',
|
|
96
|
+
},
|
|
97
|
+
liquidSpinText:{
|
|
98
|
+
id: 'creatives.componentsV2.FormBuilder.liquidSpinText',
|
|
99
|
+
defaultMessage: 'Validating the template, it might take a few seconds',
|
|
100
|
+
},
|
|
93
101
|
});
|
|
@@ -77,3 +77,11 @@ export function clearTopbarMenuData() {
|
|
|
77
77
|
export const getSupportVideosConfig = () => ({
|
|
78
78
|
type: types.GET_SUPPORT_VIDEOS_CONFIG_REQUEST,
|
|
79
79
|
});
|
|
80
|
+
|
|
81
|
+
export const getLiquidTags = (data,callback) => {
|
|
82
|
+
return{
|
|
83
|
+
type: types.GET_LIQUID_TAGS_REQUEST,
|
|
84
|
+
data,
|
|
85
|
+
callback,
|
|
86
|
+
};
|
|
87
|
+
};
|
|
@@ -59,3 +59,7 @@ export const FAILURE = 'FAILURE';
|
|
|
59
59
|
export const ENABLE_PRODUCT_SUPPORT_VIDEOS = 'ENABLE_PRODUCT_SUPPORT_VIDEOS';
|
|
60
60
|
export const DEFAULT = 'default';
|
|
61
61
|
export const DEFAULT_MODULE = 'creatives';
|
|
62
|
+
|
|
63
|
+
export const GET_LIQUID_TAGS_FAILURE = 'cap/GET_LIQUID_TAGS_FAILURE_V2';
|
|
64
|
+
export const GET_LIQUID_TAGS_REQUEST = 'cap/GET_LIQUID_TAGS_REQUEST_V2';
|
|
65
|
+
export const GET_LIQUID_TAGS_SUCCESS = 'cap/GET_LIQUID_TAGS_SUCCESS_V2';
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export const expectedStateGetLiquidTagsRequest = {
|
|
2
|
+
fetchingLiquidTags: true,
|
|
3
|
+
fetchingSchema: true,
|
|
4
|
+
fetchingSchemaError: "",
|
|
5
|
+
liquidTags: [],
|
|
6
|
+
messages: [],
|
|
7
|
+
metaEntities: {
|
|
8
|
+
layouts: [],
|
|
9
|
+
tagLookupMap: {},
|
|
10
|
+
tags: []
|
|
11
|
+
},
|
|
12
|
+
orgID: "",
|
|
13
|
+
token: ""
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const expectedStateGetLiquidTagsFailure = {
|
|
17
|
+
fetchingLiquidTags: false,
|
|
18
|
+
fetchingSchema: true,
|
|
19
|
+
fetchingSchemaError: "",
|
|
20
|
+
liquidTags: [],
|
|
21
|
+
messages: [],
|
|
22
|
+
metaEntities: {
|
|
23
|
+
layouts: [],
|
|
24
|
+
tagLookupMap: {},
|
|
25
|
+
tags: []
|
|
26
|
+
},
|
|
27
|
+
orgID: "",
|
|
28
|
+
token: ""
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const expectedStateGetLiquidTagsSuccess = {
|
|
32
|
+
fetchingLiquidTags: false,
|
|
33
|
+
fetchingSchema: true,
|
|
34
|
+
fetchingSchemaError: "",
|
|
35
|
+
liquidTags: [],
|
|
36
|
+
messages: [],
|
|
37
|
+
metaEntities: {
|
|
38
|
+
layouts: [],
|
|
39
|
+
tagLookupMap: {},
|
|
40
|
+
tags: []
|
|
41
|
+
},
|
|
42
|
+
orgID: "",
|
|
43
|
+
token: ""
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const expectedStateGetSchemaForEntitySuccessTAG = {
|
|
47
|
+
fetchingLiquidTags: false,
|
|
48
|
+
fetchingSchema: false,
|
|
49
|
+
fetchingSchemaError: false,
|
|
50
|
+
liquidTags: [],
|
|
51
|
+
messages: [],
|
|
52
|
+
metaEntities: {
|
|
53
|
+
layouts: undefined,
|
|
54
|
+
tagLookupMap: { undefined: "32" },
|
|
55
|
+
tags: { standard: { random: "32" } }
|
|
56
|
+
},
|
|
57
|
+
orgID: "",
|
|
58
|
+
token: ""
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const expectedStateGetSchemaForEntitySuccess = {
|
|
62
|
+
fetchingLiquidTags: false,
|
|
63
|
+
fetchingSchema: false,
|
|
64
|
+
fetchingSchemaError: false,
|
|
65
|
+
liquidTags: [],
|
|
66
|
+
messages: [],
|
|
67
|
+
metaEntities: {
|
|
68
|
+
layouts: undefined,
|
|
69
|
+
tagLookupMap: undefined,
|
|
70
|
+
tags: undefined
|
|
71
|
+
},
|
|
72
|
+
orgID: "",
|
|
73
|
+
token: ""
|
|
74
|
+
};
|
|
@@ -6,6 +6,7 @@ import _ from 'lodash';
|
|
|
6
6
|
import * as types from './constants';
|
|
7
7
|
import initialState from '../../initialState';
|
|
8
8
|
import { FAILURE } from '../App/constants';
|
|
9
|
+
import { TAG } from '../Whatsapp/constants';
|
|
9
10
|
|
|
10
11
|
function capReducer(state = fromJS(initialState.cap), action) {
|
|
11
12
|
switch (action.type) {
|
|
@@ -85,13 +86,24 @@ function capReducer(state = fromJS(initialState.cap), action) {
|
|
|
85
86
|
return state
|
|
86
87
|
.set('fetchingSchema', false)
|
|
87
88
|
.set('fetchingSchemaError', true);
|
|
89
|
+
case types.GET_LIQUID_TAGS_REQUEST:
|
|
90
|
+
return state
|
|
91
|
+
.set('fetchingLiquidTags', true)
|
|
92
|
+
case types.GET_LIQUID_TAGS_FAILURE:
|
|
93
|
+
return state
|
|
94
|
+
.set('fetchingLiquidTags', false)
|
|
95
|
+
case types.GET_LIQUID_TAGS_SUCCESS:
|
|
96
|
+
return state
|
|
97
|
+
.set('fetchingLiquidTags', false)
|
|
88
98
|
case types.GET_SCHEMA_FOR_ENTITY_SUCCESS: {
|
|
89
99
|
const stateMeta = state.get('metaEntities');
|
|
100
|
+
const standardTagMap = _.keyBy(action?.data?.metaEntities?.standard, item => item?.definition?.value);
|
|
90
101
|
return state
|
|
91
102
|
.set('fetchingSchema', false)
|
|
92
103
|
.set('metaEntities', {
|
|
93
104
|
layouts: action.data && action.entityType === 'LAYOUT' ? action.data.metaEntities : stateMeta.layouts,
|
|
94
105
|
tags: action.data && action.entityType === 'TAG' ? action.data.metaEntities : stateMeta.tags,
|
|
106
|
+
tagLookupMap: action?.data && action?.entityType === TAG ? standardTagMap : stateMeta?.tagLookupMap,
|
|
95
107
|
})
|
|
96
108
|
.set('fetchingSchemaError', false);
|
|
97
109
|
}
|
|
@@ -149,6 +149,30 @@ export function* fetchSchemaForEntity(queryParams) {
|
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
export function* getLiquidTags(action) {
|
|
153
|
+
try {
|
|
154
|
+
let result = yield call(Api.getLiquidTags, action?.data);
|
|
155
|
+
if (result) {
|
|
156
|
+
if (result?.errors?.length > 0) {
|
|
157
|
+
const getAskAiraErrorResponse = yield call(Api.askAiraForLiquid, {
|
|
158
|
+
templateContent: action?.data,
|
|
159
|
+
errorMessage: result?.errors?.[0]?.message,
|
|
160
|
+
});
|
|
161
|
+
if (getAskAiraErrorResponse?.result?.errors?.length) {
|
|
162
|
+
result = getAskAiraErrorResponse?.result;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (action?.callback) {
|
|
166
|
+
yield call(action?.callback, result);
|
|
167
|
+
}
|
|
168
|
+
yield put({ type: types.GET_LIQUID_TAGS_SUCCESS });
|
|
169
|
+
} else {
|
|
170
|
+
yield put({ type: types.GET_LIQUID_TAGS_FAILURE });
|
|
171
|
+
}
|
|
172
|
+
} catch (error) {
|
|
173
|
+
yield put({ type: types.GET_LIQUID_TAGS_FAILURE });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
152
176
|
const getTopbarData = (parentModule) => {
|
|
153
177
|
switch (parentModule) {
|
|
154
178
|
case LOYALTY:
|
|
@@ -193,7 +217,9 @@ export function* getSupportVideosConfig({ callback }) {
|
|
|
193
217
|
export function* watchFetchSchemaForEntity() {
|
|
194
218
|
yield takeLatest(types.GET_SCHEMA_FOR_ENTITY_REQUEST, fetchSchemaForEntity);
|
|
195
219
|
}
|
|
196
|
-
|
|
220
|
+
export function* watchLiquidEntity() {
|
|
221
|
+
yield takeLatest(types.GET_LIQUID_TAGS_REQUEST, getLiquidTags);
|
|
222
|
+
}
|
|
197
223
|
|
|
198
224
|
function* watchForOrgChange() {
|
|
199
225
|
yield takeLatest(types.SWITCH_ORG_REQUEST, switchOrg);
|
|
@@ -226,6 +252,7 @@ export default [
|
|
|
226
252
|
watchFetchSchemaForEntity,
|
|
227
253
|
watchGetTopbarMenuData,
|
|
228
254
|
watchForGetVideosConfig,
|
|
255
|
+
watchLiquidEntity,
|
|
229
256
|
];
|
|
230
257
|
|
|
231
258
|
|
|
@@ -67,6 +67,10 @@ const selectCurrentOrgDetails = () => createSelector(
|
|
|
67
67
|
return currentOrgDetails ? currentOrgDetails.toJS() : null;
|
|
68
68
|
}
|
|
69
69
|
);
|
|
70
|
+
const selectLiquidStateDetails = () => createSelector(
|
|
71
|
+
selectCapDomain,
|
|
72
|
+
(globalState) => globalState.get('fetchingLiquidTags')
|
|
73
|
+
);
|
|
70
74
|
|
|
71
75
|
const makeSelectFetchingSchema = () => createSelector(
|
|
72
76
|
selectCapDomain,
|
|
@@ -108,4 +112,5 @@ export {
|
|
|
108
112
|
makeSelectLoyaltyPromotionDisplay,
|
|
109
113
|
makeSelectFetchingSchemaError,
|
|
110
114
|
makeSelectDemoVideoAndLink,
|
|
115
|
+
selectLiquidStateDetails,
|
|
111
116
|
};
|
|
@@ -41,6 +41,7 @@ exports[`<Cap /> should render correct component 1`] = `
|
|
|
41
41
|
"clearMetaEntities": [Function],
|
|
42
42
|
"clearTopbarMenuData": [Function],
|
|
43
43
|
"fetchSchemaForEntity": [Function],
|
|
44
|
+
"getLiquidTags": [Function],
|
|
44
45
|
"getSupportVideosConfig": [Function],
|
|
45
46
|
"getTopbarMenuData": [Function],
|
|
46
47
|
"getUserData": [Function],
|