@capillarytech/creatives-library 8.0.114 → 8.0.116
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/package.json +1 -1
- package/utils/commonUtils.js +354 -4
- package/utils/tagValidations.js +22 -5
- package/utils/tests/commonUtil.test.js +605 -169
- package/utils/tests/tagValidations.test.js +129 -3
- package/v2Components/ErrorInfoNote/ErrorTypeRenderer.js +125 -0
- package/v2Components/ErrorInfoNote/ErrorTypeRenderer.test.js +147 -0
- package/v2Components/ErrorInfoNote/index.js +114 -47
- package/v2Components/ErrorInfoNote/messages.js +25 -0
- package/v2Components/ErrorInfoNote/style.scss +14 -1
- package/v2Components/ErrorInfoNote/utils.js +50 -0
- package/v2Components/ErrorInfoNote/utils.test.js +189 -0
- package/v2Components/FormBuilder/index.js +203 -127
- package/v2Components/FormBuilder/messages.js +1 -1
- package/v2Containers/Cap/reducer.js +4 -4
- package/v2Containers/Cap/sagas.js +9 -3
- package/v2Containers/Cap/tests/saga.test.js +12 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +26 -3
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +3 -1
- package/v2Containers/CreativesContainer/constants.js +4 -1
- package/v2Containers/CreativesContainer/index.js +46 -19
- package/v2Containers/CreativesContainer/messages.js +4 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +21 -3
- package/v2Containers/CreativesContainer/tests/index.test.js +1 -0
- package/v2Containers/Ebill/index.js +3 -3
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +1 -1
- package/v2Containers/InApp/index.js +126 -50
- package/v2Containers/InApp/tests/index.test.js +1 -1
- package/v2Containers/InApp/tests/sagas.test.js +1 -1
- package/v2Containers/InApp/tests/utils.test.js +85 -0
- package/v2Containers/InApp/utils.js +57 -0
- package/v2Containers/InApp/utils.test.js +70 -0
- package/v2Containers/MobilePush/Create/index.js +24 -20
- package/v2Containers/MobilePush/Edit/index.js +6 -2
- package/v2Containers/MobilepushWrapper/index.js +2 -0
- package/v2Containers/Sms/Create/index.js +1 -0
- package/v2Containers/Sms/Edit/index.js +2 -0
- package/v2Containers/SmsTrai/Edit/index.js +49 -10
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +112 -36
- package/v2Containers/SmsTrai/Edit/tests/index.test.js +1 -3
- package/v2Containers/SmsWrapper/index.js +5 -1
- package/v2Containers/Templates/sagas.js +1 -1
|
@@ -49,10 +49,10 @@ import * as actions from "../../v2Containers/Cap/actions";
|
|
|
49
49
|
import './_formBuilder.scss';
|
|
50
50
|
import {updateCharCount, checkUnicode} from "../../utils/smsCharCountV2";
|
|
51
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
|
+
import { SMS, MOBILE_PUSH, LINE, ENABLE_AI_SUGGESTIONS,AI_CONTENT_BOT_DISABLED, EMAIL, LIQUID_SUPPORTED_CHANNELS, INAPP } from '../../v2Containers/CreativesContainer/constants';
|
|
53
53
|
import globalMessages from '../../v2Containers/Cap/messages';
|
|
54
54
|
import { convert } from 'html-to-text';
|
|
55
|
-
import {
|
|
55
|
+
import { OUTBOUND } from './constants';
|
|
56
56
|
import { GET_TRANSLATION_MAPPED } from '../../containers/TagList/constants';
|
|
57
57
|
import moment from 'moment';
|
|
58
58
|
import { CUSTOMER_BARCODE_TAG , COPY_OF, ENTRY_TRIGGER_TAG_REGEX} from '../../containers/App/constants';
|
|
@@ -60,7 +60,7 @@ import { REQUEST } from '../../v2Containers/Cap/constants'
|
|
|
60
60
|
import { hasLiquidSupportFeature, isEmailUnsubscribeTagMandatory } from '../../utils/common';
|
|
61
61
|
import { isUrl } from '../../v2Containers/Line/Container/Wrapper/utils';
|
|
62
62
|
import { bindActionCreators } from 'redux';
|
|
63
|
-
|
|
63
|
+
import { getChannelData, validateLiquidTemplateContent, validateMobilePushContent } from '../../utils/commonUtils';
|
|
64
64
|
const TabPane = Tabs.TabPane;
|
|
65
65
|
const {Column} = Table;
|
|
66
66
|
const {TextArea} = CapInput;
|
|
@@ -135,7 +135,12 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
135
135
|
this.handleSetRadioValue = this.handleSetRadioValue.bind(this);
|
|
136
136
|
this.formElements = [];
|
|
137
137
|
// Check if the liquid flow feature is supported and the channel is in the supported list.
|
|
138
|
-
this.liquidFlow =
|
|
138
|
+
this.liquidFlow = this.isLiquidFlowSupported.bind(this);
|
|
139
|
+
this.onSubmitWrapper = this.onSubmitWrapper.bind(this);
|
|
140
|
+
|
|
141
|
+
}
|
|
142
|
+
isLiquidFlowSupported = () => {
|
|
143
|
+
return Boolean(LIQUID_SUPPORTED_CHANNELS.includes(this.props?.schema?.channel?.toUpperCase()) && hasLiquidSupportFeature());
|
|
139
144
|
}
|
|
140
145
|
|
|
141
146
|
componentWillMount() {
|
|
@@ -538,6 +543,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
538
543
|
} else {
|
|
539
544
|
errorData['template-name'] = false;
|
|
540
545
|
}
|
|
546
|
+
isLiquidValid = isValid;
|
|
541
547
|
}
|
|
542
548
|
|
|
543
549
|
//validation for LINE
|
|
@@ -653,20 +659,21 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
653
659
|
let isOnlyAndroid = false, isOnlyIos = false;
|
|
654
660
|
const { TAG_BRACKET_COUNT_MISMATCH_ERROR } = errorMessageForTags;
|
|
655
661
|
const androidData = {};
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
662
|
+
if (this.state.formData && this.state.formData[0]) {
|
|
663
|
+
for(let data in this.state.formData[0]){
|
|
664
|
+
if(!!this.state.formData[0][data] && data !== "tabKey" && data !== "base"){
|
|
665
|
+
androidData[data] = this.state.formData[0][data];
|
|
666
|
+
}
|
|
659
667
|
}
|
|
660
|
-
}
|
|
661
668
|
if(_.isEmpty(androidData)){
|
|
662
669
|
isOnlyIos = true;
|
|
663
670
|
}
|
|
664
671
|
const iosData = {};
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
672
|
+
for(let data in this.state.formData[1]){
|
|
673
|
+
if(!!this.state.formData[1][data] && data !== "tabKey" && data !== "base"){
|
|
674
|
+
iosData[data] = this.state.formData[1][data];
|
|
675
|
+
}
|
|
668
676
|
}
|
|
669
|
-
}
|
|
670
677
|
if(_.isEmpty(iosData)){
|
|
671
678
|
isOnlyAndroid = true;
|
|
672
679
|
}
|
|
@@ -701,17 +708,19 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
701
708
|
errorData[parseInt(index)]['invalid-tags'] = tagValidationResponse.unsupportedTags;
|
|
702
709
|
errorData[parseInt(index)][`bracket-error`] = isBraceError && TAG_BRACKET_COUNT_MISMATCH_ERROR;
|
|
703
710
|
isValid = false;
|
|
711
|
+
isCurrentTabValid = false;
|
|
704
712
|
}
|
|
705
713
|
}
|
|
706
714
|
|
|
707
715
|
} else {
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
716
|
+
|
|
717
|
+
errorData[parseInt(index)][`message-editor${selector}`] = true;
|
|
718
|
+
isValid = false;
|
|
719
|
+
isCurrentTabValid = false;
|
|
711
720
|
}
|
|
712
721
|
|
|
713
|
-
|
|
714
722
|
|
|
723
|
+
|
|
715
724
|
let title = tab[`message-title${selector}`]
|
|
716
725
|
if(title){
|
|
717
726
|
title = title.trim();
|
|
@@ -730,6 +739,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
730
739
|
errorData[parseInt(index)][`message-title${selector}`] = isBraceError ? TAG_BRACKET_COUNT_MISMATCH_ERROR : true;
|
|
731
740
|
errorData[parseInt(index)][`bracket-error`] = isBraceError && TAG_BRACKET_COUNT_MISMATCH_ERROR;
|
|
732
741
|
isValid = false;
|
|
742
|
+
isCurrentTabValid = false;
|
|
733
743
|
}
|
|
734
744
|
}
|
|
735
745
|
} else {
|
|
@@ -858,9 +868,9 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
858
868
|
isCurrentTabValid = false;
|
|
859
869
|
}else {
|
|
860
870
|
errorData[parseInt(index)][key] = false;
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
});
|
|
864
874
|
}
|
|
865
875
|
}
|
|
866
876
|
if(index == 0){
|
|
@@ -873,7 +883,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
873
883
|
errorData['template-name'] = true;
|
|
874
884
|
isValid = false;
|
|
875
885
|
androidValid = false;
|
|
876
|
-
iosValid = false
|
|
886
|
+
iosValid = false;
|
|
877
887
|
} else {
|
|
878
888
|
errorData['template-name'] = false;
|
|
879
889
|
}
|
|
@@ -893,7 +903,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
893
903
|
// modal.body = "Android template is not configured. Save without Android template";
|
|
894
904
|
// modal.id = "ios";
|
|
895
905
|
// modal.type = 'confirm';
|
|
896
|
-
return;
|
|
906
|
+
return; //returning from here will not call the liquid middleware
|
|
897
907
|
}
|
|
898
908
|
const iosData = {};
|
|
899
909
|
for(let data in this.state.formData[1]){
|
|
@@ -909,7 +919,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
909
919
|
// modal.id = 'android';
|
|
910
920
|
// modal.type = 'confirm';
|
|
911
921
|
// this.setState({modal, showModal: true, androidValid, iosValid});
|
|
912
|
-
return;
|
|
922
|
+
return; //returning from here will not call the liquid middleware
|
|
913
923
|
}
|
|
914
924
|
}
|
|
915
925
|
if (!androidValid || !iosValid) {
|
|
@@ -922,7 +932,9 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
922
932
|
} else if (!isOnlyIos && !isOnlyAndroid && androidValid && iosValid) {
|
|
923
933
|
isValid = isValid && true;
|
|
924
934
|
}
|
|
935
|
+
isLiquidValid = isValid;
|
|
925
936
|
}
|
|
937
|
+
}
|
|
926
938
|
|
|
927
939
|
// Form Data Validation for Email Channel
|
|
928
940
|
if (this.props?.schema?.channel?.toUpperCase() === EMAIL) {
|
|
@@ -983,7 +995,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
983
995
|
errorData[index][currentLang]['template-content'] = true;
|
|
984
996
|
isValid = false;
|
|
985
997
|
isLiquidValid = false;
|
|
986
|
-
if ((showMessages && !isNaN(index)) || this.liquidFlow) {
|
|
998
|
+
if ((showMessages && !isNaN(index)) || this.liquidFlow()) {
|
|
987
999
|
if (tagValidationResponse?.missingTags?.length > 0 || tagValidationResponse?.unsupportedTags?.length > 0) {
|
|
988
1000
|
errorString += `${this.props.intl.formatMessage(messages.contentNotValidLanguage)} ${currentLang}\n`;
|
|
989
1001
|
}
|
|
@@ -999,7 +1011,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
999
1011
|
if (tagValidationResponse?.isContentEmpty) {
|
|
1000
1012
|
errorString += this.props.intl.formatMessage(messages.emailBodyEmptyError);
|
|
1001
1013
|
// Adds a bypass for cases where content is initially empty in the creation flow.
|
|
1002
|
-
if(this.liquidFlow){
|
|
1014
|
+
if(this.liquidFlow()){
|
|
1003
1015
|
errorString = "";
|
|
1004
1016
|
isLiquidValid = true;
|
|
1005
1017
|
}
|
|
@@ -1007,8 +1019,8 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1007
1019
|
}
|
|
1008
1020
|
}
|
|
1009
1021
|
}
|
|
1010
|
-
if (errorString
|
|
1011
|
-
if (this.liquidFlow) {
|
|
1022
|
+
if (errorString) {
|
|
1023
|
+
if (this.liquidFlow()) {
|
|
1012
1024
|
this.setState(
|
|
1013
1025
|
(prevState) => ({
|
|
1014
1026
|
liquidErrorMessage: {
|
|
@@ -1018,7 +1030,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1018
1030
|
}),
|
|
1019
1031
|
() => {
|
|
1020
1032
|
// Callback after the state is updated
|
|
1021
|
-
this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage);
|
|
1033
|
+
this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage, this.props.channel === SMS? null: this.state.currentTab);
|
|
1022
1034
|
}
|
|
1023
1035
|
);
|
|
1024
1036
|
} else {
|
|
@@ -1034,7 +1046,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1034
1046
|
});
|
|
1035
1047
|
}
|
|
1036
1048
|
|
|
1037
|
-
const isTemplateValid = this.liquidFlow ? isLiquidValid : isValid;
|
|
1049
|
+
const isTemplateValid = this.liquidFlow() ? isLiquidValid : isValid;
|
|
1038
1050
|
//Updating the state with the error data
|
|
1039
1051
|
this.setState((prevState) => ({
|
|
1040
1052
|
isFormValid: isTemplateValid,
|
|
@@ -1046,126 +1058,174 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1046
1058
|
},
|
|
1047
1059
|
errorData,
|
|
1048
1060
|
}), () => {
|
|
1049
|
-
this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage);
|
|
1061
|
+
this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage, this.props.channel === SMS? null: this.state.currentTab);
|
|
1062
|
+
|
|
1050
1063
|
if (onValidationComplete) {
|
|
1064
|
+
|
|
1051
1065
|
onValidationComplete();
|
|
1052
1066
|
}
|
|
1053
1067
|
});
|
|
1054
1068
|
|
|
1069
|
+
|
|
1055
1070
|
this.props.onFormValidityChange(isTemplateValid, errorData);
|
|
1056
1071
|
return isTemplateValid;
|
|
1057
|
-
|
|
1072
|
+
}
|
|
1058
1073
|
|
|
1059
1074
|
indexOfEnd(targetString, string) {
|
|
1060
1075
|
var io = targetString.indexOf(string);
|
|
1061
1076
|
return io == -1 ? -1 : io + string.length;
|
|
1062
1077
|
}
|
|
1063
|
-
continueSaveForm = (id) => {
|
|
1064
1078
|
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
...prevState?.formData
|
|
1073
|
-
|
|
1074
|
-
...prevState?.formData?.base
|
|
1075
|
-
|
|
1079
|
+
handleLiquidTemplateSubmit = (templateContent) => {
|
|
1080
|
+
const channel = this.props.channel || this.props?.schema?.channel?.toUpperCase();
|
|
1081
|
+
|
|
1082
|
+
if (templateContent && channel === EMAIL) {
|
|
1083
|
+
this.setState((prevState) => {
|
|
1084
|
+
return {
|
|
1085
|
+
formData: {
|
|
1086
|
+
...prevState?.formData,
|
|
1087
|
+
base: {
|
|
1088
|
+
...prevState?.formData?.base,
|
|
1089
|
+
[this.props.baseLanguage]: {
|
|
1090
|
+
...prevState?.formData?.base?.[this.props.baseLanguage],
|
|
1091
|
+
"template-content": preprocessHtml(templateContent)
|
|
1092
|
+
}
|
|
1076
1093
|
}
|
|
1077
1094
|
}
|
|
1095
|
+
};
|
|
1096
|
+
}, () => {
|
|
1097
|
+
if (this.props.onSubmit) {
|
|
1098
|
+
this.props.onSubmit(this.state.formData);
|
|
1078
1099
|
}
|
|
1079
|
-
};
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
|
|
1100
|
+
});
|
|
1101
|
+
} else {
|
|
1102
|
+
// For all other channels
|
|
1103
|
+
this.props.onSubmit(this.state.formData);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
onSubmitWrapper = (args) => {
|
|
1107
|
+
const {singleTab = null} = args || {};
|
|
1108
|
+
if (this.liquidFlow()) {
|
|
1109
|
+
// For MPUSH, we need to validate both Android and iOS content separately
|
|
1110
|
+
if (this.props.channel === MOBILE_PUSH || this.props?.schema?.channel?.toUpperCase() === MOBILE_PUSH) {
|
|
1111
|
+
this.validateFormBuilderMPush(this.state.formData, singleTab);
|
|
1112
|
+
return;
|
|
1083
1113
|
}
|
|
1084
|
-
|
|
1114
|
+
|
|
1115
|
+
// For other channels (EMAIL, SMS, INAPP)
|
|
1116
|
+
const content = getChannelData(this.props.schema.channel || this.props.channel, this.state.formData, this.props.baseLanguage);
|
|
1117
|
+
|
|
1118
|
+
// Set up callbacks for error and success handling
|
|
1119
|
+
const onError = ({ standardErrors, liquidErrors }) => {
|
|
1120
|
+
this.setState(
|
|
1121
|
+
(prevState) => ({
|
|
1122
|
+
liquidErrorMessage: {
|
|
1123
|
+
...prevState.liquidErrorMessage,
|
|
1124
|
+
STANDARD_ERROR_MSG: standardErrors,
|
|
1125
|
+
LIQUID_ERROR_MSG: liquidErrors,
|
|
1126
|
+
},
|
|
1127
|
+
}),
|
|
1128
|
+
() => {
|
|
1129
|
+
this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage);
|
|
1130
|
+
this.props.stopValidation();
|
|
1131
|
+
}
|
|
1132
|
+
);
|
|
1133
|
+
};
|
|
1134
|
+
|
|
1135
|
+
const onSuccess = (contentToSubmit) => {
|
|
1136
|
+
this.handleLiquidTemplateSubmit(contentToSubmit);
|
|
1137
|
+
};
|
|
1138
|
+
|
|
1139
|
+
// Call the common validation function
|
|
1140
|
+
validateLiquidTemplateContent(content, {
|
|
1141
|
+
getLiquidTags: this.props.actions.getLiquidTags,
|
|
1142
|
+
formatMessage: this.props.intl.formatMessage,
|
|
1143
|
+
messages,
|
|
1144
|
+
onError,
|
|
1145
|
+
onSuccess,
|
|
1146
|
+
tagLookupMap: this.props?.metaEntities?.tagLookupMap,
|
|
1147
|
+
eventContextTags: this.props?.eventContextTags,
|
|
1148
|
+
isLiquidFlow: this.liquidFlow(),
|
|
1149
|
+
forwardedTags: this.props?.isLoyaltyModule ? this.props?.forwardedTags : {},
|
|
1150
|
+
skipTags: this.skipTags.bind(this)
|
|
1151
|
+
});
|
|
1152
|
+
} else {
|
|
1153
|
+
this.props.onSubmit(this.state.formData);
|
|
1154
|
+
}
|
|
1085
1155
|
}
|
|
1086
|
-
|
|
1156
|
+
|
|
1087
1157
|
saveForm(saveForm) {
|
|
1088
1158
|
if (this.props.isNewVersionFlow && !saveForm) {
|
|
1089
1159
|
this.props.getValidationData();
|
|
1090
1160
|
return;
|
|
1091
1161
|
}
|
|
1092
1162
|
if ( this.state.isFormValid ) {
|
|
1093
|
-
|
|
1094
|
-
const templateContent = this.state.formData?.base?.en?.["template-content"] || "";
|
|
1095
|
-
//Converts given HTML content to plain text string.
|
|
1096
|
-
const content = convert(
|
|
1097
|
-
templateContent, GLOBAL_CONVERT_OPTIONS
|
|
1098
|
-
);
|
|
1099
|
-
/*
|
|
1100
|
-
The `handleResult` function is used as a callback for `getLiquidTags` to handle the results post-processing.
|
|
1101
|
-
It checks for errors and unsupported tags in the fetched liquid tags, displays any necessary validation messages,
|
|
1102
|
-
and proceeds with form submission if all checks pass.
|
|
1103
|
-
*/
|
|
1104
|
-
const handleResult = (result) => {
|
|
1105
|
-
const {formatMessage} = this.props.intl;
|
|
1106
|
-
// Checks for errors in the result and displays them if any are found.
|
|
1107
|
-
const validString = /\S/.test(content);
|
|
1108
|
-
// If the content is empty or there are errors in the result, display the necessary validation messages.
|
|
1109
|
-
if (result?.errors?.length > 0 || !validString) {
|
|
1110
|
-
this.setState((prevState) => ({
|
|
1111
|
-
liquidErrorMessage: {
|
|
1112
|
-
...prevState.liquidErrorMessage,
|
|
1113
|
-
STANDARD_ERROR_MSG: !validString ? [...(prevState.liquidErrorMessage?.STANDARD_ERROR_MSG || []), formatMessage(messages.emailBodyEmptyError)] : prevState.liquidErrorMessage?.STANDARD_ERROR_MSG,
|
|
1114
|
-
LIQUID_ERROR_MSG: result?.errors?.map(error => error?.message) ?? [formatMessage(messages.somethingWentWrong)],
|
|
1115
|
-
}
|
|
1116
|
-
}), () => {
|
|
1117
|
-
this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage);
|
|
1118
|
-
});
|
|
1119
|
-
this.props.stopValidation();
|
|
1120
|
-
return;
|
|
1121
|
-
}
|
|
1122
|
-
// Extracts the supported liquid tags from the given content and do the necessary checks.
|
|
1123
|
-
else {
|
|
1124
|
-
const extractedLiquidTags = extractNames(result?.data || []);
|
|
1125
|
-
// Extracts the supported liquid tags from the given content.
|
|
1126
|
-
const supportedLiquidTags = checkSupport(
|
|
1127
|
-
result,
|
|
1128
|
-
this.props?.metaEntities?.tagLookupMap,
|
|
1129
|
-
this.props?.eventContextTags,
|
|
1130
|
-
this.liquidFlow,
|
|
1131
|
-
this.props?.isLoyaltyModule ? this.props?.forwardedTags : {}
|
|
1132
|
-
);
|
|
1133
|
-
const unsupportedLiquidTags = extractedLiquidTags?.filter(
|
|
1134
|
-
(tag) => !supportedLiquidTags?.includes(tag) && !this.skipTags(tag)
|
|
1135
|
-
);
|
|
1136
|
-
if (
|
|
1137
|
-
unsupportedLiquidTags?.length > 0
|
|
1138
|
-
) {
|
|
1139
|
-
this.setState(
|
|
1140
|
-
(prevState) => ({
|
|
1141
|
-
liquidErrorMessage: {
|
|
1142
|
-
...prevState.liquidErrorMessage,
|
|
1143
|
-
LIQUID_ERROR_MSG: [formatMessage(messages.unsupportedTagsValidationError, {
|
|
1144
|
-
unsupportedTags: unsupportedLiquidTags.join(", ")})],
|
|
1145
|
-
},
|
|
1146
|
-
}),
|
|
1147
|
-
() => {
|
|
1148
|
-
this.props.showLiquidErrorInFooter(
|
|
1149
|
-
this.state.liquidErrorMessage
|
|
1150
|
-
);
|
|
1151
|
-
}
|
|
1152
|
-
);
|
|
1153
|
-
this.props.stopValidation();
|
|
1154
|
-
return;
|
|
1155
|
-
}
|
|
1156
|
-
this.handleLiquidTemplateSubmit(templateContent);
|
|
1157
|
-
}
|
|
1158
|
-
};
|
|
1159
|
-
this.props.actions.getLiquidTags(preprocessHtml(content), handleResult);
|
|
1160
|
-
} else {
|
|
1161
|
-
this.props.onSubmit(this.state.formData);
|
|
1162
|
-
}
|
|
1163
|
+
this.onSubmitWrapper();
|
|
1163
1164
|
} else {
|
|
1164
1165
|
this.setState({checkValidation: true});
|
|
1165
1166
|
this.validateForm(null, null, true);
|
|
1166
1167
|
}
|
|
1167
1168
|
}
|
|
1168
1169
|
|
|
1170
|
+
|
|
1171
|
+
// New function to handle MPUSH content validation for both Android and iOS
|
|
1172
|
+
validateFormBuilderMPush = (formData,singleTab) => {
|
|
1173
|
+
const currentTab = this.state.currentTab;
|
|
1174
|
+
|
|
1175
|
+
// Set up callbacks for error and success handling
|
|
1176
|
+
const onLiquidError = ({ standardErrors, liquidErrors }) => {
|
|
1177
|
+
|
|
1178
|
+
this.setState(
|
|
1179
|
+
(prevState) => ({
|
|
1180
|
+
liquidErrorMessage: {
|
|
1181
|
+
...prevState.liquidErrorMessage,
|
|
1182
|
+
STANDARD_ERROR_MSG: standardErrors,
|
|
1183
|
+
LIQUID_ERROR_MSG: liquidErrors,
|
|
1184
|
+
},
|
|
1185
|
+
}),
|
|
1186
|
+
() => {
|
|
1187
|
+
this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage, this.state.currentTab);
|
|
1188
|
+
this.props.stopValidation();
|
|
1189
|
+
}
|
|
1190
|
+
);
|
|
1191
|
+
};
|
|
1192
|
+
|
|
1193
|
+
const onSuccess = (contentToSubmit) => {
|
|
1194
|
+
// Clear any previous errors
|
|
1195
|
+
this.setState(
|
|
1196
|
+
(prevState) => ({
|
|
1197
|
+
liquidErrorMessage: {
|
|
1198
|
+
...prevState.liquidErrorMessage,
|
|
1199
|
+
STANDARD_ERROR_MSG: [],
|
|
1200
|
+
LIQUID_ERROR_MSG: [],
|
|
1201
|
+
},
|
|
1202
|
+
}),
|
|
1203
|
+
() => {
|
|
1204
|
+
// Format depends on the tabType
|
|
1205
|
+
this.handleLiquidTemplateSubmit(contentToSubmit);
|
|
1206
|
+
}
|
|
1207
|
+
);
|
|
1208
|
+
};
|
|
1209
|
+
|
|
1210
|
+
// Call the utility function with our callbacks
|
|
1211
|
+
validateMobilePushContent(formData, {
|
|
1212
|
+
currentTab,
|
|
1213
|
+
onError: onLiquidError,
|
|
1214
|
+
onSuccess,
|
|
1215
|
+
getLiquidTags: this.props.actions.getLiquidTags,
|
|
1216
|
+
formatMessage: this.props.intl.formatMessage,
|
|
1217
|
+
messages: messages,
|
|
1218
|
+
tagLookupMap: this.props?.metaEntities?.tagLookupMap,
|
|
1219
|
+
eventContextTags: this.props?.eventContextTags,
|
|
1220
|
+
isLiquidFlow: this.liquidFlow(), // Use the method instead of props
|
|
1221
|
+
forwardedTags: this.props?.isLoyaltyModule ? this.props?.forwardedTags : {},
|
|
1222
|
+
skipTags: this.skipTags.bind(this),
|
|
1223
|
+
extractNames,
|
|
1224
|
+
checkSupport,
|
|
1225
|
+
singleTab: singleTab?.toUpperCase(),
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1169
1229
|
transformInjectedTags(tags) {
|
|
1170
1230
|
_.forEach(tags, (tag) => {
|
|
1171
1231
|
const temp = tag;
|
|
@@ -1304,7 +1364,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1304
1364
|
});
|
|
1305
1365
|
|
|
1306
1366
|
ifSupported = ifSupported || this.checkIfSupportedTag(tagValue, injectedTags);
|
|
1307
|
-
if (!ifSupported && !this.liquidFlow) {
|
|
1367
|
+
if (!ifSupported && !this.liquidFlow()) {
|
|
1308
1368
|
response.unsupportedTags.push(tagValue);
|
|
1309
1369
|
response.valid = false;
|
|
1310
1370
|
}
|
|
@@ -2278,12 +2338,11 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
2278
2338
|
};
|
|
2279
2339
|
handleOk = (ev) => {
|
|
2280
2340
|
const id = ev.target.id;
|
|
2281
|
-
|
|
2282
2341
|
this.setState({showModal: false});
|
|
2283
2342
|
if (id === "android" && this.state.androidValid) {
|
|
2284
|
-
this.
|
|
2343
|
+
this.onSubmitWrapper({singleTab: id});
|
|
2285
2344
|
} else if (id === "ios" && this.state.iosValid) {
|
|
2286
|
-
this.
|
|
2345
|
+
this.onSubmitWrapper({singleTab: id});
|
|
2287
2346
|
} else if (id === "sms-version-modal") {
|
|
2288
2347
|
this.state.currentEvent.call(this.props.parent, this.state.currentEventData, this.state.currentTab);
|
|
2289
2348
|
this.setState({currentEvent: {}, currentEventData: {}});
|
|
@@ -2483,6 +2542,19 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
2483
2542
|
default:
|
|
2484
2543
|
break;
|
|
2485
2544
|
}
|
|
2545
|
+
const prevErrorMessage = this.state.liquidErrorMessage?.STANDARD_ERROR_MSG?.[0];
|
|
2546
|
+
if (prevErrorMessage !== errorMessageText && errorMessageText && this.liquidFlow()) {
|
|
2547
|
+
this.setState((prevState) => ({
|
|
2548
|
+
liquidErrorMessage: {
|
|
2549
|
+
...prevState.liquidErrorMessage,
|
|
2550
|
+
STANDARD_ERROR_MSG: [errorMessageText],
|
|
2551
|
+
}
|
|
2552
|
+
}), () => {
|
|
2553
|
+
|
|
2554
|
+
this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage, this.props.channel === SMS? null: this.state.currentTab);
|
|
2555
|
+
});
|
|
2556
|
+
}
|
|
2557
|
+
else{
|
|
2486
2558
|
if (styling === 'semantic') {
|
|
2487
2559
|
columns.push(
|
|
2488
2560
|
<CapColumn key="input" span={val.width} offset={offset}>
|
|
@@ -2490,7 +2562,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
2490
2562
|
id={val.id}
|
|
2491
2563
|
placeholder={val.placeholder ? val.placeholder : ''}
|
|
2492
2564
|
className={`${ifError ? 'error-form-builder' : ''}`}
|
|
2493
|
-
errorMessage={errorMessageText}
|
|
2565
|
+
errorMessage={errorMessageText && !this.liquidFlow() ? errorMessageText : ''}
|
|
2494
2566
|
label={val.label}
|
|
2495
2567
|
autosize={val.autosize ? val.autosizeParams : false}
|
|
2496
2568
|
onChange={(e) => this.updateFormData(e.target.value, val)}
|
|
@@ -2520,8 +2592,10 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
2520
2592
|
</CapColumn>
|
|
2521
2593
|
);
|
|
2522
2594
|
}
|
|
2595
|
+
}
|
|
2523
2596
|
};
|
|
2524
2597
|
|
|
2598
|
+
|
|
2525
2599
|
renderColLabelSection(section, childIndex) {
|
|
2526
2600
|
// const schema = this.props.schema
|
|
2527
2601
|
const fields = [];
|
|
@@ -3465,7 +3539,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3465
3539
|
<CapColumn
|
|
3466
3540
|
style={val.colStyle ? val.colStyle : {border : ""}}
|
|
3467
3541
|
span={val.width}
|
|
3468
|
-
className={`${(this.state.liquidErrorMessage?.LIQUID_ERROR_MSG?.length || this.state.liquidErrorMessage?.STANDARD_ERROR_MSG?.length) && this.liquidFlow && "error-boundary"} `}
|
|
3542
|
+
className={`${(this.state.liquidErrorMessage?.LIQUID_ERROR_MSG?.length || this.state.liquidErrorMessage?.STANDARD_ERROR_MSG?.length) && this.liquidFlow() && "error-boundary"} `}
|
|
3469
3543
|
>
|
|
3470
3544
|
<CKEditor
|
|
3471
3545
|
id={val.id}
|
|
@@ -3509,7 +3583,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3509
3583
|
isModuleFilterEnabled = this.props.isFullMode;
|
|
3510
3584
|
}
|
|
3511
3585
|
columns.push(
|
|
3512
|
-
<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"}`}>
|
|
3586
|
+
<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"}`}>
|
|
3513
3587
|
<BeeEditor
|
|
3514
3588
|
uid={uuid}
|
|
3515
3589
|
tokenData={beeToken}
|
|
@@ -3795,7 +3869,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3795
3869
|
|
|
3796
3870
|
|
|
3797
3871
|
return (
|
|
3798
|
-
<CapSpin spinning={Boolean((this.liquidFlow && this.props.liquidExtractionInProgress) || this.props.metaDataStatus === REQUEST)} tip={this.props.intl.formatMessage(messages.liquidSpinText)} >
|
|
3872
|
+
<CapSpin spinning={Boolean((this.liquidFlow() && this.props.liquidExtractionInProgress) || this.props.metaDataStatus === REQUEST)} tip={this.props.intl.formatMessage(messages.liquidSpinText)} >
|
|
3799
3873
|
<CapRow>
|
|
3800
3874
|
{this.props.schema && this.renderForm()}
|
|
3801
3875
|
<SlideBox
|
|
@@ -3877,7 +3951,8 @@ FormBuilder.propTypes = {
|
|
|
3877
3951
|
isFullMode: PropTypes.bool,
|
|
3878
3952
|
currentOrgDetails: PropTypes.object,
|
|
3879
3953
|
liquidExtractionInProgress: PropTypes.bool,
|
|
3880
|
-
showLiquidErrorInFooter: PropTypes.func
|
|
3954
|
+
showLiquidErrorInFooter: PropTypes.func,
|
|
3955
|
+
channel: PropTypes.string,
|
|
3881
3956
|
};
|
|
3882
3957
|
|
|
3883
3958
|
const mapStateToProps = createStructuredSelector({
|
|
@@ -3894,3 +3969,4 @@ function mapDispatchToProps(dispatch) {
|
|
|
3894
3969
|
}
|
|
3895
3970
|
|
|
3896
3971
|
export default connect(mapStateToProps,mapDispatchToProps)(injectIntl(FormBuilder));
|
|
3972
|
+
|
|
@@ -94,7 +94,7 @@ export default defineMessages({
|
|
|
94
94
|
id: 'creatives.componentsV2.FormBuilder.somethingWentWrong',
|
|
95
95
|
defaultMessage: 'Something went wrong while validating content, please try again later',
|
|
96
96
|
},
|
|
97
|
-
liquidSpinText:{
|
|
97
|
+
liquidSpinText: {
|
|
98
98
|
id: 'creatives.componentsV2.FormBuilder.liquidSpinText',
|
|
99
99
|
defaultMessage: 'Validating the template, it might take a few seconds',
|
|
100
100
|
},
|
|
@@ -90,13 +90,13 @@ function capReducer(state = fromJS(initialState.cap), action) {
|
|
|
90
90
|
.set('fetchingSchemaError', true);
|
|
91
91
|
case types.GET_LIQUID_TAGS_REQUEST:
|
|
92
92
|
return state
|
|
93
|
-
.set('fetchingLiquidTags', true)
|
|
93
|
+
.set('fetchingLiquidTags', true);
|
|
94
94
|
case types.GET_LIQUID_TAGS_FAILURE:
|
|
95
95
|
return state
|
|
96
|
-
.set('fetchingLiquidTags', false)
|
|
97
|
-
case types.GET_LIQUID_TAGS_SUCCESS:
|
|
96
|
+
.set('fetchingLiquidTags', false);
|
|
97
|
+
case types.GET_LIQUID_TAGS_SUCCESS:
|
|
98
98
|
return state
|
|
99
|
-
.set('fetchingLiquidTags', false)
|
|
99
|
+
.set('fetchingLiquidTags', false);
|
|
100
100
|
case types.GET_SCHEMA_FOR_ENTITY_SUCCESS: {
|
|
101
101
|
//Process standard tags
|
|
102
102
|
const standardTagMapInitial = _.keyBy(
|
|
@@ -155,7 +155,8 @@ export function* fetchSchemaForEntity(queryParams) {
|
|
|
155
155
|
|
|
156
156
|
export function* getLiquidTags(action) {
|
|
157
157
|
try {
|
|
158
|
-
|
|
158
|
+
const askAiraResponse = {askAiraResponse: {}, isError: true};
|
|
159
|
+
const result = yield call(Api.getLiquidTags, action?.data);
|
|
159
160
|
if (result) {
|
|
160
161
|
if (result?.errors?.length > 0) {
|
|
161
162
|
const getAskAiraErrorResponse = yield call(Api.askAiraForLiquid, {
|
|
@@ -163,11 +164,16 @@ export function* getLiquidTags(action) {
|
|
|
163
164
|
errorMessage: result?.errors?.[0]?.message,
|
|
164
165
|
});
|
|
165
166
|
if (getAskAiraErrorResponse?.result?.errors?.length) {
|
|
166
|
-
|
|
167
|
+
askAiraResponse.askAiraResponse = getAskAiraErrorResponse?.result;
|
|
168
|
+
} else {
|
|
169
|
+
askAiraResponse.askAiraResponse = result;
|
|
167
170
|
}
|
|
171
|
+
} else {
|
|
172
|
+
askAiraResponse.askAiraResponse = result;
|
|
173
|
+
askAiraResponse.isError = false;
|
|
168
174
|
}
|
|
169
175
|
if (action?.callback) {
|
|
170
|
-
yield call(action?.callback,
|
|
176
|
+
yield call(action?.callback, askAiraResponse);
|
|
171
177
|
}
|
|
172
178
|
yield put({ type: types.GET_LIQUID_TAGS_SUCCESS });
|
|
173
179
|
} else {
|
|
@@ -350,6 +350,18 @@ describe('getLiquidTags saga', () => {
|
|
|
350
350
|
.put({ type: GET_LIQUID_TAGS_FAILURE, error: true })
|
|
351
351
|
.run();
|
|
352
352
|
});
|
|
353
|
+
it('handles API response with errors', () => {
|
|
354
|
+
const action = { data: 'templateContent' };
|
|
355
|
+
const result = { data: [{ message: 'Error message' }] };
|
|
356
|
+
|
|
357
|
+
expectSaga(getLiquidTags, action)
|
|
358
|
+
.provide([
|
|
359
|
+
[matchers.call.fn(api.getLiquidTags, 'templateContent'), result],
|
|
360
|
+
])
|
|
361
|
+
.call.fn(action.callback, result)
|
|
362
|
+
.put({ type: GET_LIQUID_TAGS_SUCCESS })
|
|
363
|
+
.run();
|
|
364
|
+
});
|
|
353
365
|
it('handles API response with undefined', () => {
|
|
354
366
|
const action = { data: 'templateContent' };
|
|
355
367
|
const result = undefined;
|