@capillarytech/creatives-library 8.0.9 → 8.0.11
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 +20 -0
- package/services/tests/api.test.js +148 -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 +171 -70
- 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/Line/Container/sagas.js +1 -1
- package/v2Containers/MobilePush/Edit/constants.js +2 -0
- package/v2Containers/MobilePush/Edit/index.js +91 -24
- 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,11 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
95
98
|
customPopoverVisible: false,
|
|
96
99
|
isDrawerVisible: false,
|
|
97
100
|
translationLang: 'en',
|
|
101
|
+
showLiquidValidation: false,
|
|
102
|
+
liquidErrorMessage: {
|
|
103
|
+
STANDARD_ERROR_MSG: [],
|
|
104
|
+
LIQUID_ERROR_MSG: [],
|
|
105
|
+
},
|
|
98
106
|
};
|
|
99
107
|
this.renderForm = this.renderForm.bind(this);
|
|
100
108
|
this.updateFormData = this.updateFormData.bind(this);
|
|
@@ -126,6 +134,8 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
126
134
|
this.transformInjectedTags = this.transformInjectedTags.bind(this);
|
|
127
135
|
this.handleSetRadioValue = this.handleSetRadioValue.bind(this);
|
|
128
136
|
this.formElements = [];
|
|
137
|
+
// Check if the liquid flow feature is supported and the channel is in the supported list.
|
|
138
|
+
this.liquidFlow = hasLiquidSupportFeature() && LIQUID_SUPPORTED_CHANNELS.includes(props?.schema?.channel?.toUpperCase());
|
|
129
139
|
}
|
|
130
140
|
|
|
131
141
|
componentWillMount() {
|
|
@@ -480,6 +490,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
480
490
|
validateForm(tags, injectedTags, isSave, showMessages, onValidationComplete) {
|
|
481
491
|
const channel = _.get(this, "props.schema.channel");
|
|
482
492
|
let isValid = true;
|
|
493
|
+
let isLiquidValid = true;
|
|
483
494
|
const type = (this.props.location && this.props.location.query.type) ? this.props.location.query.type : 'full';
|
|
484
495
|
const currentModule = (this.props.location && this.props.location.query.module) ? this.props.location.query.module : 'default';
|
|
485
496
|
let errorData = _.cloneDeep(this.state.errorData);
|
|
@@ -915,10 +926,10 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
915
926
|
}
|
|
916
927
|
|
|
917
928
|
// Form Data Validation for Email Channel
|
|
918
|
-
if (this.props
|
|
929
|
+
if (this.props?.schema?.channel?.toUpperCase() === EMAIL) {
|
|
919
930
|
// Validating Email By iterating all elements of FormBuilder
|
|
920
931
|
const isEmail = true;
|
|
921
|
-
_.forEach(this.state.formData,
|
|
932
|
+
_.forEach(this.state.formData,(data, index) => {
|
|
922
933
|
|
|
923
934
|
if (!this.state.formData["template-subject"] && type.toLowerCase() === 'embedded' && (currentModule.toLowerCase() === 'loyalty' || currentModule.toLowerCase() === 'dvs' || currentModule.toLowerCase() === 'timeline' || currentModule.toLowerCase() === 'library')) {
|
|
924
935
|
errorData["template-subject"] = true;
|
|
@@ -963,37 +974,59 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
963
974
|
if (!content) {
|
|
964
975
|
return false;
|
|
965
976
|
}
|
|
966
|
-
const tagValidationResponse = this.validateTags(content, tags, injectedTags, isEmail)
|
|
977
|
+
const tagValidationResponse = this.validateTags(content, tags, injectedTags, isEmail);
|
|
967
978
|
if (errorData[index][currentLang] === undefined) {
|
|
968
979
|
errorData[index][currentLang] = {};
|
|
969
980
|
}
|
|
970
|
-
|
|
971
|
-
if (tagValidationResponse.valid) {
|
|
981
|
+
if (tagValidationResponse?.valid) {
|
|
972
982
|
errorData[index][currentLang]['template-content'] = false;
|
|
973
983
|
} else {
|
|
974
984
|
errorData[index][currentLang]['template-content'] = true;
|
|
975
985
|
isValid = false;
|
|
976
|
-
|
|
977
|
-
|
|
986
|
+
isLiquidValid = false;
|
|
987
|
+
if ((showMessages && !isNaN(index)) || this.liquidFlow) {
|
|
988
|
+
if (tagValidationResponse?.missingTags?.length > 0 || tagValidationResponse?.unsupportedTags?.length > 0) {
|
|
978
989
|
errorString += `${this.props.intl.formatMessage(messages.contentNotValidLanguage)} ${currentLang}\n`;
|
|
979
990
|
}
|
|
980
|
-
if (tagValidationResponse
|
|
991
|
+
if (tagValidationResponse?.missingTags?.length > 0) {
|
|
981
992
|
errorString += `${this.props.intl.formatMessage(messages.missingTags)} ${tagValidationResponse.missingTags.toString()}\n`;
|
|
982
993
|
}
|
|
983
|
-
if (tagValidationResponse
|
|
994
|
+
if (tagValidationResponse?.unsupportedTags?.length > 0) {
|
|
984
995
|
errorString += `${this.props.intl.formatMessage(messages.unsupportedTags)} ${tagValidationResponse.unsupportedTags.toString()}\n`;
|
|
985
996
|
}
|
|
986
|
-
if (tagValidationResponse
|
|
997
|
+
if (tagValidationResponse?.isBraceError){
|
|
987
998
|
errorString += this.props.intl.formatMessage(globalMessages.unbalanacedCurlyBraces);
|
|
988
999
|
}
|
|
989
1000
|
if (tagValidationResponse?.isContentEmpty) {
|
|
990
1001
|
errorString += this.props.intl.formatMessage(messages.emailBodyEmptyError);
|
|
1002
|
+
// Adds a bypass for cases where content is initially empty in the creation flow.
|
|
1003
|
+
if(this.liquidFlow && !this.props.isEdit){
|
|
1004
|
+
errorString = "";
|
|
1005
|
+
isValid = true;
|
|
1006
|
+
isLiquidValid = true;
|
|
1007
|
+
}
|
|
991
1008
|
}
|
|
992
1009
|
}
|
|
993
1010
|
}
|
|
994
1011
|
}
|
|
995
1012
|
if (errorString !== "") {
|
|
996
|
-
this.
|
|
1013
|
+
if (this.liquidFlow) {
|
|
1014
|
+
this.setState(
|
|
1015
|
+
(prevState) => ({
|
|
1016
|
+
showLiquidValidation: true,
|
|
1017
|
+
liquidErrorMessage: {
|
|
1018
|
+
...prevState.liquidErrorMessage,
|
|
1019
|
+
STANDARD_ERROR_MSG: [errorString],
|
|
1020
|
+
},
|
|
1021
|
+
}),
|
|
1022
|
+
() => {
|
|
1023
|
+
// Callback after the state is updated
|
|
1024
|
+
this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage);
|
|
1025
|
+
}
|
|
1026
|
+
);
|
|
1027
|
+
} else {
|
|
1028
|
+
this.openNotificationWithIcon('error', errorString, "email-validation-error");
|
|
1029
|
+
}
|
|
997
1030
|
}
|
|
998
1031
|
_.forEach(errorData[index], (versionData, key) => {
|
|
999
1032
|
if (data.selectedLanguages.indexOf(key) === -1) {
|
|
@@ -1002,32 +1035,20 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1002
1035
|
});
|
|
1003
1036
|
}
|
|
1004
1037
|
});
|
|
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
1038
|
}
|
|
1030
|
-
|
|
1039
|
+
//Updating the state with the error data
|
|
1040
|
+
this.setState((prevState) => ({
|
|
1041
|
+
isFormValid: isValid,
|
|
1042
|
+
showLiquidValidation: prevState.liquidErrorMessage?.LIQUID_ERROR_MSG?.length > 0 ? true : !isLiquidValid,
|
|
1043
|
+
liquidErrorMessage: {
|
|
1044
|
+
//Do not update the LIQUID_ERROR_MSG validation error message
|
|
1045
|
+
...prevState.liquidErrorMessage,
|
|
1046
|
+
//update Standard error message based on the updated content
|
|
1047
|
+
STANDARD_ERROR_MSG: isValid ? [] : prevState.liquidErrorMessage?.STANDARD_ERROR_MSG,
|
|
1048
|
+
},
|
|
1049
|
+
errorData,
|
|
1050
|
+
}), () => {
|
|
1051
|
+
this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage);
|
|
1031
1052
|
if (onValidationComplete) {
|
|
1032
1053
|
onValidationComplete();
|
|
1033
1054
|
}
|
|
@@ -1049,8 +1070,70 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1049
1070
|
this.props.getValidationData();
|
|
1050
1071
|
return;
|
|
1051
1072
|
}
|
|
1052
|
-
if (this.state.isFormValid) {
|
|
1053
|
-
|
|
1073
|
+
if ( this.state.isFormValid ) {
|
|
1074
|
+
if (this.liquidFlow) {
|
|
1075
|
+
//Converts given HTML content to plain text string.
|
|
1076
|
+
const content = convert(
|
|
1077
|
+
this.state.formData?.base?.en?.["template-content"]
|
|
1078
|
+
);
|
|
1079
|
+
/*
|
|
1080
|
+
The `handleResult` function is used as a callback for `getLiquidTags` to handle the results post-processing.
|
|
1081
|
+
It checks for errors and unsupported tags in the fetched liquid tags, displays any necessary validation messages,
|
|
1082
|
+
and proceeds with form submission if all checks pass.
|
|
1083
|
+
*/
|
|
1084
|
+
const handleResult = (result) => {
|
|
1085
|
+
const {formatMessage} = this.props.intl;
|
|
1086
|
+
if (result?.errors?.length > 0) {
|
|
1087
|
+
this.setState((prevState) => ({
|
|
1088
|
+
showLiquidValidation: true,
|
|
1089
|
+
liquidErrorMessage: {
|
|
1090
|
+
...prevState.liquidErrorMessage,
|
|
1091
|
+
LIQUID_ERROR_MSG: result?.errors?.map(error => error?.message) ?? [formatMessage(messages.somethingWentWrong)],
|
|
1092
|
+
},
|
|
1093
|
+
}) , () => {
|
|
1094
|
+
this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage);
|
|
1095
|
+
});
|
|
1096
|
+
this.props.stopValidation();
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
else if (result?.data?.length > 0) {
|
|
1100
|
+
const extractedLiquidTags = extractNames(result?.data);
|
|
1101
|
+
// Extracts the supported liquid tags from the given content.
|
|
1102
|
+
const supportedLiquidTags = checkSupport(
|
|
1103
|
+
result,
|
|
1104
|
+
this.props?.metaEntities?.tagLookupMap
|
|
1105
|
+
);
|
|
1106
|
+
const unsupportedLiquidTags = extractedLiquidTags?.filter(
|
|
1107
|
+
(tag) => !supportedLiquidTags?.includes(tag)
|
|
1108
|
+
);
|
|
1109
|
+
if (
|
|
1110
|
+
unsupportedLiquidTags?.length > 0
|
|
1111
|
+
) {
|
|
1112
|
+
this.setState(
|
|
1113
|
+
(prevState) => ({
|
|
1114
|
+
showLiquidValidation: true,
|
|
1115
|
+
liquidErrorMessage: {
|
|
1116
|
+
...prevState.liquidErrorMessage,
|
|
1117
|
+
LIQUID_ERROR_MSG: [formatMessage(messages.unsupportedTagsValidationError, {
|
|
1118
|
+
unsupportedTags: unsupportedLiquidTags.join(", ")})],
|
|
1119
|
+
},
|
|
1120
|
+
}),
|
|
1121
|
+
() => {
|
|
1122
|
+
this.props.showLiquidErrorInFooter(
|
|
1123
|
+
this.state.liquidErrorMessage
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
);
|
|
1127
|
+
this.props.stopValidation();
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
this.props.onSubmit(this.state.formData);
|
|
1131
|
+
}
|
|
1132
|
+
};
|
|
1133
|
+
this.props.actions.getLiquidTags(preprocessHtml(content), handleResult);
|
|
1134
|
+
} else {
|
|
1135
|
+
this.props.onSubmit(this.state.formData);
|
|
1136
|
+
}
|
|
1054
1137
|
} else {
|
|
1055
1138
|
this.setState({checkValidation: true});
|
|
1056
1139
|
this.validateForm(null, null, true);
|
|
@@ -1092,7 +1175,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1092
1175
|
});
|
|
1093
1176
|
|
|
1094
1177
|
return result;
|
|
1095
|
-
}
|
|
1178
|
+
};
|
|
1096
1179
|
|
|
1097
1180
|
skipTags(tag) {
|
|
1098
1181
|
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 +1234,12 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1151
1234
|
if (isEmailUnsubscribeTagMandatory() && isEmail && this.props?.moduleType === OUTBOUND) {
|
|
1152
1235
|
const missingTagIndex = response?.missingTags?.indexOf("unsubscribe");
|
|
1153
1236
|
if(missingTagIndex != -1) { //skip regex tags for mandatory tags also
|
|
1154
|
-
response?.missingTags?.splice(missingTagIndex, 1);
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1237
|
+
response?.missingTags?.splice(missingTagIndex, 1);
|
|
1238
|
+
}
|
|
1239
|
+
if (validString) {
|
|
1240
|
+
response.valid = true;
|
|
1241
|
+
} else {
|
|
1242
|
+
response.isContentEmpty = true;
|
|
1160
1243
|
}
|
|
1161
1244
|
}
|
|
1162
1245
|
while (match !== null ) {
|
|
@@ -1183,11 +1266,12 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1183
1266
|
}
|
|
1184
1267
|
}
|
|
1185
1268
|
ifSupported = ifSupported || this.checkIfSupportedTag(tagValue, injectedTags);
|
|
1186
|
-
if (!ifSupported) {
|
|
1269
|
+
if (!ifSupported && !this.liquidFlow) {
|
|
1187
1270
|
response.unsupportedTags.push(tagValue);
|
|
1188
1271
|
response.valid = false;
|
|
1189
1272
|
}
|
|
1190
|
-
|
|
1273
|
+
|
|
1274
|
+
if (response?.unsupportedTags?.length == 0 && response?.missingTags?.length == 0 ) {
|
|
1191
1275
|
response.valid = true;
|
|
1192
1276
|
}
|
|
1193
1277
|
}
|
|
@@ -3338,7 +3422,11 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3338
3422
|
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
3423
|
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
3424
|
columns.push(
|
|
3341
|
-
<CapColumn
|
|
3425
|
+
<CapColumn
|
|
3426
|
+
style={val.colStyle ? val.colStyle : {border : ""}}
|
|
3427
|
+
span={val.width}
|
|
3428
|
+
className={`${this.state.showLiquidValidation && this.liquidFlow && "error-boundary"} `}
|
|
3429
|
+
>
|
|
3342
3430
|
<CKEditor
|
|
3343
3431
|
id={val.id}
|
|
3344
3432
|
content={content}
|
|
@@ -3381,7 +3469,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3381
3469
|
isModuleFilterEnabled = this.props.isFullMode;
|
|
3382
3470
|
}
|
|
3383
3471
|
columns.push(
|
|
3384
|
-
<CapColumn style={val.colStyle ? val.colStyle : {}} span={val.width}>
|
|
3472
|
+
<CapColumn style={val.colStyle ? val.colStyle : {}} span={val.width} className={`${this.state.showLiquidValidation && this.liquidFlow && "error-boundary"}`}>
|
|
3385
3473
|
<BeeEditor
|
|
3386
3474
|
uid={uuid}
|
|
3387
3475
|
tokenData={beeToken}
|
|
@@ -3485,7 +3573,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3485
3573
|
fields.push(row);
|
|
3486
3574
|
}
|
|
3487
3575
|
});
|
|
3488
|
-
if (!this.props.isEmailLoading && this.props.schema.channel.toUpperCase() ===
|
|
3576
|
+
if (!this.props.isEmailLoading && this.props.schema.channel.toUpperCase() === EMAIL && !fields.length) {
|
|
3489
3577
|
return (
|
|
3490
3578
|
<div style={{ textAlign: 'center' }}>
|
|
3491
3579
|
<CapSpin spinning />
|
|
@@ -3666,30 +3754,32 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3666
3754
|
|
|
3667
3755
|
|
|
3668
3756
|
return (
|
|
3757
|
+
<CapSpin spinning={this.liquidFlow && this.props.liquidExtractionInProgress} tip={this.props.intl.formatMessage(messages.liquidSpinText)} >
|
|
3669
3758
|
<CapRow>
|
|
3670
|
-
{
|
|
3671
|
-
this.props.schema ? this.renderForm() : ''
|
|
3672
|
-
}
|
|
3759
|
+
{this.props.schema && this.renderForm()}
|
|
3673
3760
|
<SlideBox
|
|
3674
|
-
header={
|
|
3761
|
+
header={
|
|
3762
|
+
<h3>{this.props.intl.formatMessage(messages.layoutSelection)}</h3>
|
|
3763
|
+
}
|
|
3675
3764
|
width={60}
|
|
3676
3765
|
content={cmsTemplateSelectionContent}
|
|
3677
3766
|
show={this.props.showEdmEmailTemplates}
|
|
3678
3767
|
handleClose={this.props.toggleEdmEmailTemplateSelection}
|
|
3679
|
-
loadingText={this.props.intl.formatMessage(
|
|
3768
|
+
loadingText={this.props.intl.formatMessage(
|
|
3769
|
+
messages.loadingEDMTemplates
|
|
3770
|
+
)}
|
|
3680
3771
|
isLoading={this.props.getCmsTemplatesInProgress}
|
|
3681
3772
|
/>
|
|
3682
|
-
{
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
)
|
|
3691
|
-
}
|
|
3773
|
+
{this.props.capDrawerContent && (
|
|
3774
|
+
<CapDrawer
|
|
3775
|
+
content={this.props.capDrawerContent}
|
|
3776
|
+
visible={this.state.isDrawerVisible}
|
|
3777
|
+
width={380}
|
|
3778
|
+
onClose={() => this.props.setDrawerVisibility(false)}
|
|
3779
|
+
/>
|
|
3780
|
+
)}
|
|
3692
3781
|
</CapRow>
|
|
3782
|
+
</CapSpin>
|
|
3693
3783
|
);
|
|
3694
3784
|
}
|
|
3695
3785
|
}
|
|
@@ -3697,6 +3787,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3697
3787
|
FormBuilder.defaultProps = {
|
|
3698
3788
|
isNewVersionFlow: false,
|
|
3699
3789
|
userLocale: localStorage.getItem('jlocale') || 'en',
|
|
3790
|
+
showLiquidErrorInFooter: () => {},
|
|
3700
3791
|
};
|
|
3701
3792
|
|
|
3702
3793
|
FormBuilder.propTypes = {
|
|
@@ -3743,10 +3834,20 @@ FormBuilder.propTypes = {
|
|
|
3743
3834
|
capDrawerContent: PropTypes.array,
|
|
3744
3835
|
isFullMode: PropTypes.bool,
|
|
3745
3836
|
currentOrgDetails: PropTypes.object,
|
|
3837
|
+
liquidExtractionInProgress: PropTypes.bool,
|
|
3838
|
+
showLiquidErrorInFooter: PropTypes.func,
|
|
3746
3839
|
};
|
|
3747
3840
|
|
|
3748
3841
|
const mapStateToProps = createStructuredSelector({
|
|
3749
3842
|
currentOrgDetails: selectCurrentOrgDetails(),
|
|
3843
|
+
liquidExtractionInProgress: selectLiquidStateDetails(),
|
|
3844
|
+
metaEntities: makeSelectMetaEntities(),
|
|
3750
3845
|
});
|
|
3751
3846
|
|
|
3752
|
-
|
|
3847
|
+
function mapDispatchToProps(dispatch) {
|
|
3848
|
+
return {
|
|
3849
|
+
actions: bindActionCreators(actions, dispatch),
|
|
3850
|
+
};
|
|
3851
|
+
}
|
|
3852
|
+
|
|
3853
|
+
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],
|