@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.
Files changed (42) hide show
  1. package/package.json +1 -1
  2. package/utils/commonUtils.js +354 -4
  3. package/utils/tagValidations.js +22 -5
  4. package/utils/tests/commonUtil.test.js +605 -169
  5. package/utils/tests/tagValidations.test.js +129 -3
  6. package/v2Components/ErrorInfoNote/ErrorTypeRenderer.js +125 -0
  7. package/v2Components/ErrorInfoNote/ErrorTypeRenderer.test.js +147 -0
  8. package/v2Components/ErrorInfoNote/index.js +114 -47
  9. package/v2Components/ErrorInfoNote/messages.js +25 -0
  10. package/v2Components/ErrorInfoNote/style.scss +14 -1
  11. package/v2Components/ErrorInfoNote/utils.js +50 -0
  12. package/v2Components/ErrorInfoNote/utils.test.js +189 -0
  13. package/v2Components/FormBuilder/index.js +203 -127
  14. package/v2Components/FormBuilder/messages.js +1 -1
  15. package/v2Containers/Cap/reducer.js +4 -4
  16. package/v2Containers/Cap/sagas.js +9 -3
  17. package/v2Containers/Cap/tests/saga.test.js +12 -0
  18. package/v2Containers/CreativesContainer/SlideBoxContent.js +26 -3
  19. package/v2Containers/CreativesContainer/SlideBoxFooter.js +3 -1
  20. package/v2Containers/CreativesContainer/constants.js +4 -1
  21. package/v2Containers/CreativesContainer/index.js +46 -19
  22. package/v2Containers/CreativesContainer/messages.js +4 -0
  23. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +21 -3
  24. package/v2Containers/CreativesContainer/tests/index.test.js +1 -0
  25. package/v2Containers/Ebill/index.js +3 -3
  26. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +1 -1
  27. package/v2Containers/InApp/index.js +126 -50
  28. package/v2Containers/InApp/tests/index.test.js +1 -1
  29. package/v2Containers/InApp/tests/sagas.test.js +1 -1
  30. package/v2Containers/InApp/tests/utils.test.js +85 -0
  31. package/v2Containers/InApp/utils.js +57 -0
  32. package/v2Containers/InApp/utils.test.js +70 -0
  33. package/v2Containers/MobilePush/Create/index.js +24 -20
  34. package/v2Containers/MobilePush/Edit/index.js +6 -2
  35. package/v2Containers/MobilepushWrapper/index.js +2 -0
  36. package/v2Containers/Sms/Create/index.js +1 -0
  37. package/v2Containers/Sms/Edit/index.js +2 -0
  38. package/v2Containers/SmsTrai/Edit/index.js +49 -10
  39. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +112 -36
  40. package/v2Containers/SmsTrai/Edit/tests/index.test.js +1 -3
  41. package/v2Containers/SmsWrapper/index.js +5 -1
  42. 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 { GLOBAL_CONVERT_OPTIONS, OUTBOUND } from './constants';
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 = Boolean(LIQUID_SUPPORTED_CHANNELS.includes(props?.schema?.channel?.toUpperCase()) && hasLiquidSupportFeature());
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
- for(let data in this.state.formData[0]){
657
- if(!!this.state.formData[0][data] && data !== "tabKey" && data !== "base"){
658
- androidData[data] = this.state.formData[0][data];
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
- for(let data in this.state.formData[1]){
666
- if(!!this.state.formData[1][data] && data !== "tabKey" && data !== "base"){
667
- iosData[data] = this.state.formData[1][data];
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
- errorData[parseInt(index)][`message-editor${selector}`] = true;
709
- isValid = false;
710
- isCurrentTabValid = false;
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
- handleLiquidTemplateSubmit =(templateContent) => {
1067
- if(templateContent){this.setState((prevState) => {
1068
- return {
1069
- formData: {
1070
- ...prevState?.formData,
1071
- base: {
1072
- ...prevState?.formData?.base,
1073
- en: {
1074
- ...prevState?.formData?.base?.en,
1075
- "template-content": preprocessHtml(templateContent)
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
- if (this.props.onSubmit) {
1082
- this.props.onSubmit(this.state.formData);
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
- if (this.liquidFlow) {
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.props.onSubmit(this.state.formData);
2343
+ this.onSubmitWrapper({singleTab: id});
2285
2344
  } else if (id === "ios" && this.state.iosValid) {
2286
- this.props.onSubmit(this.state.formData);
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
- let result = yield call(Api.getLiquidTags, action?.data);
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
- result = getAskAiraErrorResponse?.result;
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, result);
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;