@capillarytech/creatives-library 8.0.113 → 8.0.114-alpha.0

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 (106) hide show
  1. package/containers/App/test/saga.test.js +1 -1
  2. package/containers/Assets/Gallery/tests/__snapshots__/reducer.test.js.snap +1 -1
  3. package/containers/Assets/Gallery/tests/actions.test.js +2 -3
  4. package/containers/Assets/Gallery/tests/reducer.test.js +7 -7
  5. package/containers/Assets/Gallery/tests/saga.test.js +9 -9
  6. package/containers/Dashboard/test/saga.test.js +1 -1
  7. package/containers/Ebill/test/saga.test.js +1 -1
  8. package/containers/Email/test/saga.test.js +33 -33
  9. package/containers/LanguageProvider/tests/actions.test.js +1 -1
  10. package/containers/LanguageProvider/tests/reducer.test.js +2 -2
  11. package/containers/LanguageProvider/tests/selectors.test.js +1 -1
  12. package/containers/Line/Create/tests/saga.test.js +2 -9
  13. package/containers/Line/Edit/test/saga.test.js +10 -14
  14. package/containers/MobilePush/Create/test/saga.test.js +2 -2
  15. package/containers/MobilePush/Edit/tests/saga.test.js +14 -14
  16. package/containers/Sms/Create/test/saga.test.js +4 -5
  17. package/containers/Sms/Edit/test/saga.test.js +1 -1
  18. package/containers/Templates/test/saga.test.js +14 -17
  19. package/containers/WeChat/MapTemplates/test/saga.test.js +9 -13
  20. package/containers/WeChat/RichmediaTemplates/Create/test/saga.test.js +1 -1
  21. package/containers/WeChat/RichmediaTemplates/Edit/test/saga.test.js +1 -1
  22. package/package.json +1 -1
  23. package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +8 -9
  24. package/utils/commonUtils.js +389 -3
  25. package/utils/tagValidations.js +20 -5
  26. package/utils/tests/authWrapper.test.js +2 -2
  27. package/utils/tests/cdnTransformation.test.js +16 -15
  28. package/utils/tests/commonUtil.test.js +324 -178
  29. package/v2Components/CapVideoUpload/tests/index.test.js +1 -1
  30. package/v2Components/CapWhatsappCTA/tests/index.test.js +1 -2
  31. package/v2Components/ErrorInfoNote/ErrorTypeRenderer.js +130 -0
  32. package/v2Components/ErrorInfoNote/ErrorTypeRenderer.test.js +146 -0
  33. package/v2Components/ErrorInfoNote/index.js +114 -46
  34. package/v2Components/ErrorInfoNote/messages.js +25 -0
  35. package/v2Components/ErrorInfoNote/style.scss +14 -1
  36. package/v2Components/ErrorInfoNote/utils.js +28 -0
  37. package/v2Components/ErrorInfoNote/utils.test.js +93 -0
  38. package/v2Components/FormBuilder/index.js +200 -127
  39. package/v2Components/FormBuilder/messages.js +1 -1
  40. package/v2Components/MarketingObjective/test/index.test.js +1 -1
  41. package/v2Components/NavigationBar/tests/saga.test.js +2 -3
  42. package/v2Containers/Assets/Gallery/tests/__snapshots__/reducer.test.js.snap +1 -1
  43. package/v2Containers/Assets/Gallery/tests/actions.test.js +2 -3
  44. package/v2Containers/Assets/Gallery/tests/reducer.test.js +7 -7
  45. package/v2Containers/Assets/Gallery/tests/saga.test.js +2 -2
  46. package/v2Containers/BeeEditor/test/saga.test.js +1 -1
  47. package/v2Containers/CallTask/test/saga.test.js +1 -1
  48. package/v2Containers/Cap/reducer.js +4 -4
  49. package/v2Containers/Cap/tests/actions.test.js +1 -1
  50. package/v2Containers/Cap/tests/reducer.test.js +11 -11
  51. package/v2Containers/Cap/tests/saga.test.js +1 -1
  52. package/v2Containers/Cap/tests/selectors.test.js +3 -3
  53. package/v2Containers/CapFacebookPreview/tests/saga.test.js +1 -1
  54. package/v2Containers/CreativesContainer/SlideBoxContent.js +23 -3
  55. package/v2Containers/CreativesContainer/SlideBoxFooter.js +3 -1
  56. package/v2Containers/CreativesContainer/constants.js +2 -1
  57. package/v2Containers/CreativesContainer/index.js +37 -10
  58. package/v2Containers/CreativesContainer/messages.js +4 -0
  59. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +21 -3
  60. package/v2Containers/Ebill/index.js +3 -3
  61. package/v2Containers/Ebill/test/saga.test.js +1 -1
  62. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +4 -4
  63. package/v2Containers/Email/tests/actions.test.js +1 -1
  64. package/v2Containers/Email/tests/reducer.test.js +2 -2
  65. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +1 -1
  66. package/v2Containers/FTP/test/saga.test.js +1 -1
  67. package/v2Containers/Facebook/test/saga.test.js +7 -7
  68. package/v2Containers/InApp/index.js +127 -49
  69. package/v2Containers/InApp/tests/action.test.js +7 -7
  70. package/v2Containers/InApp/tests/index.test.js +2 -4
  71. package/v2Containers/InApp/tests/reducer.test.js +175 -89
  72. package/v2Containers/InApp/tests/sagas.test.js +1 -1
  73. package/v2Containers/InApp/utils.js +37 -0
  74. package/v2Containers/LanguageProvider/tests/actions.test.js +1 -1
  75. package/v2Containers/LanguageProvider/tests/reducer.test.js +3 -3
  76. package/v2Containers/LanguageProvider/tests/saga.test.js +2 -2
  77. package/v2Containers/LanguageProvider/tests/selectors.test.js +1 -1
  78. package/v2Containers/Line/Container/ImageCarousel/tests/utils.test.js +3 -3
  79. package/v2Containers/Line/Container/Sticker/tests/utils.test.js +6 -6
  80. package/v2Containers/MobilePush/Create/index.js +24 -20
  81. package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
  82. package/v2Containers/MobilePush/Edit/index.js +4 -1
  83. package/v2Containers/MobilePush/Edit/test/saga.test.js +14 -14
  84. package/v2Containers/MobilepushWrapper/index.js +2 -0
  85. package/v2Containers/Rcs/tests/saga.test.js +1 -1
  86. package/v2Containers/Sms/Create/index.js +14 -2
  87. package/v2Containers/Sms/Create/test/saga.test.js +1 -1
  88. package/v2Containers/Sms/Edit/index.js +2 -0
  89. package/v2Containers/Sms/Edit/test/saga.test.js +5 -5
  90. package/v2Containers/SmsTrai/Create/tests/saga.test.js +1 -1
  91. package/v2Containers/SmsTrai/Create/tests/selectors.test.js +1 -1
  92. package/v2Containers/SmsWrapper/index.js +2 -0
  93. package/v2Containers/TagList/tests/TagList.test.js +1 -3
  94. package/v2Containers/TagList/tests/utils.test.js +3 -3
  95. package/v2Containers/Templates/tests/actions.test.js +1 -1
  96. package/v2Containers/Templates/tests/reducer.test.js +8 -8
  97. package/v2Containers/Templates/tests/sagas.test.js +2 -4
  98. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -13
  99. package/v2Containers/WeChat/RichmediaTemplates/Create/test/saga.test.js +1 -1
  100. package/v2Containers/WeChat/RichmediaTemplates/Edit/test/saga.test.js +1 -1
  101. package/v2Containers/Whatsapp/tests/__snapshots__/utils.test.js.snap +9 -9
  102. package/v2Containers/Whatsapp/tests/actions.test.js +3 -3
  103. package/v2Containers/Whatsapp/tests/reducer.test.js +32 -36
  104. package/v2Containers/Whatsapp/tests/utils.test.js +19 -19
  105. package/v2Containers/Zalo/tests/actions.test.js +3 -3
  106. package/v2Containers/Zalo/tests/reducer.test.js +72 -42
@@ -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,9 +135,13 @@ 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());
139
- }
138
+ this.liquidFlow = this.isLiquidFlowSupported.bind(this);
139
+ this.onSubmitWrapper = this.onSubmitWrapper.bind(this);
140
140
 
141
+ }
142
+ isLiquidFlowSupported = () => {
143
+ return Boolean(LIQUID_SUPPORTED_CHANNELS.includes(this.props?.schema?.channel?.toUpperCase()) && hasLiquidSupportFeature());
144
+ }
141
145
  componentWillMount() {
142
146
  this.setState({usingTabContainer: this.props.usingTabContainer ? this.props.usingTabContainer : false});
143
147
  if (this.props.formData && (this.props.isEdit || (!this.props.isEdit && !_.isEmpty(this.props.formData)))) {
@@ -538,6 +542,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
538
542
  } else {
539
543
  errorData['template-name'] = false;
540
544
  }
545
+ isLiquidValid = isValid;
541
546
  }
542
547
 
543
548
  //validation for LINE
@@ -653,20 +658,21 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
653
658
  let isOnlyAndroid = false, isOnlyIos = false;
654
659
  const { TAG_BRACKET_COUNT_MISMATCH_ERROR } = errorMessageForTags;
655
660
  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];
661
+ if (this.state.formData && this.state.formData[0]) {
662
+ for(let data in this.state.formData[0]){
663
+ if(!!this.state.formData[0][data] && data !== "tabKey" && data !== "base"){
664
+ androidData[data] = this.state.formData[0][data];
665
+ }
659
666
  }
660
- }
661
667
  if(_.isEmpty(androidData)){
662
668
  isOnlyIos = true;
663
669
  }
664
670
  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];
671
+ for(let data in this.state.formData[1]){
672
+ if(!!this.state.formData[1][data] && data !== "tabKey" && data !== "base"){
673
+ iosData[data] = this.state.formData[1][data];
674
+ }
668
675
  }
669
- }
670
676
  if(_.isEmpty(iosData)){
671
677
  isOnlyAndroid = true;
672
678
  }
@@ -701,17 +707,19 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
701
707
  errorData[parseInt(index)]['invalid-tags'] = tagValidationResponse.unsupportedTags;
702
708
  errorData[parseInt(index)][`bracket-error`] = isBraceError && TAG_BRACKET_COUNT_MISMATCH_ERROR;
703
709
  isValid = false;
710
+ isCurrentTabValid = false;
704
711
  }
705
712
  }
706
713
 
707
714
  } else {
708
- errorData[parseInt(index)][`message-editor${selector}`] = true;
709
- isValid = false;
710
- isCurrentTabValid = false;
715
+
716
+ errorData[parseInt(index)][`message-editor${selector}`] = true;
717
+ isValid = false;
718
+ isCurrentTabValid = false;
711
719
  }
712
720
 
713
-
714
721
 
722
+
715
723
  let title = tab[`message-title${selector}`]
716
724
  if(title){
717
725
  title = title.trim();
@@ -730,6 +738,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
730
738
  errorData[parseInt(index)][`message-title${selector}`] = isBraceError ? TAG_BRACKET_COUNT_MISMATCH_ERROR : true;
731
739
  errorData[parseInt(index)][`bracket-error`] = isBraceError && TAG_BRACKET_COUNT_MISMATCH_ERROR;
732
740
  isValid = false;
741
+ isCurrentTabValid = false;
733
742
  }
734
743
  }
735
744
  } else {
@@ -858,9 +867,9 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
858
867
  isCurrentTabValid = false;
859
868
  }else {
860
869
  errorData[parseInt(index)][key] = false;
861
- }
862
- }
863
- });
870
+ }
871
+ }
872
+ });
864
873
  }
865
874
  }
866
875
  if(index == 0){
@@ -873,7 +882,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
873
882
  errorData['template-name'] = true;
874
883
  isValid = false;
875
884
  androidValid = false;
876
- iosValid = false
885
+ iosValid = false;
877
886
  } else {
878
887
  errorData['template-name'] = false;
879
888
  }
@@ -893,7 +902,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
893
902
  // modal.body = "Android template is not configured. Save without Android template";
894
903
  // modal.id = "ios";
895
904
  // modal.type = 'confirm';
896
- return;
905
+ return; //returning from here will not call the liquid middleware
897
906
  }
898
907
  const iosData = {};
899
908
  for(let data in this.state.formData[1]){
@@ -909,7 +918,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
909
918
  // modal.id = 'android';
910
919
  // modal.type = 'confirm';
911
920
  // this.setState({modal, showModal: true, androidValid, iosValid});
912
- return;
921
+ return; //returning from here will not call the liquid middleware
913
922
  }
914
923
  }
915
924
  if (!androidValid || !iosValid) {
@@ -922,7 +931,9 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
922
931
  } else if (!isOnlyIos && !isOnlyAndroid && androidValid && iosValid) {
923
932
  isValid = isValid && true;
924
933
  }
934
+ isLiquidValid = isValid;
925
935
  }
936
+ }
926
937
 
927
938
  // Form Data Validation for Email Channel
928
939
  if (this.props?.schema?.channel?.toUpperCase() === EMAIL) {
@@ -983,7 +994,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
983
994
  errorData[index][currentLang]['template-content'] = true;
984
995
  isValid = false;
985
996
  isLiquidValid = false;
986
- if ((showMessages && !isNaN(index)) || this.liquidFlow) {
997
+ if ((showMessages && !isNaN(index)) || this.liquidFlow()) {
987
998
  if (tagValidationResponse?.missingTags?.length > 0 || tagValidationResponse?.unsupportedTags?.length > 0) {
988
999
  errorString += `${this.props.intl.formatMessage(messages.contentNotValidLanguage)} ${currentLang}\n`;
989
1000
  }
@@ -999,7 +1010,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
999
1010
  if (tagValidationResponse?.isContentEmpty) {
1000
1011
  errorString += this.props.intl.formatMessage(messages.emailBodyEmptyError);
1001
1012
  // Adds a bypass for cases where content is initially empty in the creation flow.
1002
- if(this.liquidFlow){
1013
+ if(this.liquidFlow()){
1003
1014
  errorString = "";
1004
1015
  isLiquidValid = true;
1005
1016
  }
@@ -1007,8 +1018,8 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
1007
1018
  }
1008
1019
  }
1009
1020
  }
1010
- if (errorString !== "") {
1011
- if (this.liquidFlow) {
1021
+ if (errorString) {
1022
+ if (this.liquidFlow()) {
1012
1023
  this.setState(
1013
1024
  (prevState) => ({
1014
1025
  liquidErrorMessage: {
@@ -1018,7 +1029,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
1018
1029
  }),
1019
1030
  () => {
1020
1031
  // Callback after the state is updated
1021
- this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage);
1032
+ this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage, this.props.channel === SMS? null: this.state.currentTab);
1022
1033
  }
1023
1034
  );
1024
1035
  } else {
@@ -1034,7 +1045,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
1034
1045
  });
1035
1046
  }
1036
1047
 
1037
- const isTemplateValid = this.liquidFlow ? isLiquidValid : isValid;
1048
+ const isTemplateValid = this.liquidFlow() ? isLiquidValid : isValid;
1038
1049
  //Updating the state with the error data
1039
1050
  this.setState((prevState) => ({
1040
1051
  isFormValid: isTemplateValid,
@@ -1046,126 +1057,172 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
1046
1057
  },
1047
1058
  errorData,
1048
1059
  }), () => {
1049
- this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage);
1060
+ this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage, this.props.channel === SMS? null: this.state.currentTab);
1061
+
1050
1062
  if (onValidationComplete) {
1063
+
1051
1064
  onValidationComplete();
1052
1065
  }
1053
1066
  });
1054
1067
 
1068
+
1055
1069
  this.props.onFormValidityChange(isTemplateValid, errorData);
1056
1070
  return isTemplateValid;
1057
- }
1071
+ }
1058
1072
 
1059
1073
  indexOfEnd(targetString, string) {
1060
1074
  var io = targetString.indexOf(string);
1061
1075
  return io == -1 ? -1 : io + string.length;
1062
1076
  }
1063
- continueSaveForm = (id) => {
1064
1077
 
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)
1078
+ handleLiquidTemplateSubmit = (templateContent) => {
1079
+ const channel = this.props.channel || this.props?.schema?.channel?.toUpperCase();
1080
+
1081
+ if (templateContent && channel === EMAIL) {
1082
+ this.setState((prevState) => {
1083
+ return {
1084
+ formData: {
1085
+ ...prevState?.formData,
1086
+ base: {
1087
+ ...prevState?.formData?.base,
1088
+ en: {
1089
+ ...prevState?.formData?.base?.en,
1090
+ "template-content": preprocessHtml(templateContent)
1091
+ }
1076
1092
  }
1077
1093
  }
1094
+ };
1095
+ }, () => {
1096
+ if (this.props.onSubmit) {
1097
+ this.props.onSubmit(this.state.formData);
1078
1098
  }
1079
- };
1080
- }, () => {
1081
- if (this.props.onSubmit) {
1082
- this.props.onSubmit(this.state.formData);
1099
+ });
1100
+ } else {
1101
+ // For all other channels
1102
+ this.props.onSubmit(this.state.formData);
1103
+ }
1104
+ }
1105
+ onSubmitWrapper = () => {
1106
+ if (this.liquidFlow()) {
1107
+ // For MPUSH, we need to validate both Android and iOS content separately
1108
+ if (this.props.channel === MOBILE_PUSH || this.props?.schema?.channel?.toUpperCase() === MOBILE_PUSH) {
1109
+ this.validateFormBuilderMPush(this.state.formData);
1110
+ return;
1083
1111
  }
1084
- });}
1112
+
1113
+ // For other channels (EMAIL, SMS, INAPP)
1114
+ const content = getChannelData(this.props.schema.channel || this.props.channel, this.state.formData);
1115
+
1116
+ // Set up callbacks for error and success handling
1117
+ const onError = ({ standardErrors, liquidErrors }) => {
1118
+ this.setState(
1119
+ (prevState) => ({
1120
+ liquidErrorMessage: {
1121
+ ...prevState.liquidErrorMessage,
1122
+ STANDARD_ERROR_MSG: standardErrors,
1123
+ LIQUID_ERROR_MSG: liquidErrors,
1124
+ },
1125
+ }),
1126
+ () => {
1127
+ this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage);
1128
+ this.props.stopValidation();
1129
+ }
1130
+ );
1131
+ };
1132
+
1133
+ const onSuccess = (contentToSubmit) => {
1134
+ this.handleLiquidTemplateSubmit(contentToSubmit);
1135
+ };
1136
+
1137
+ // Call the common validation function
1138
+ validateLiquidTemplateContent(content, this.props.schema.channel || this.props.channel, {
1139
+ getLiquidTags: this.props.actions.getLiquidTags,
1140
+ formatMessage: this.props.intl.formatMessage,
1141
+ messages,
1142
+ onError,
1143
+ onSuccess,
1144
+ tagLookupMap: this.props?.metaEntities?.tagLookupMap,
1145
+ eventContextTags: this.props?.eventContextTags,
1146
+ isLiquidFlow: this.liquidFlow(),
1147
+ forwardedTags: this.props?.isLoyaltyModule ? this.props?.forwardedTags : {},
1148
+ skipTags: this.skipTags.bind(this)
1149
+ });
1150
+ } else {
1151
+ this.props.onSubmit(this.state.formData);
1152
+ }
1085
1153
  }
1086
-
1154
+
1087
1155
  saveForm(saveForm) {
1088
1156
  if (this.props.isNewVersionFlow && !saveForm) {
1089
1157
  this.props.getValidationData();
1090
1158
  return;
1091
1159
  }
1092
1160
  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
- }
1161
+ this.onSubmitWrapper();
1162
+
1163
1163
  } else {
1164
1164
  this.setState({checkValidation: true});
1165
1165
  this.validateForm(null, null, true);
1166
1166
  }
1167
1167
  }
1168
1168
 
1169
+
1170
+ // New function to handle MPUSH content validation for both Android and iOS
1171
+ validateFormBuilderMPush = (formData) => {
1172
+ const currentTab = this.state.currentTab;
1173
+
1174
+ // Set up callbacks for error and success handling
1175
+ const onLiquidError = ({ standardErrors, liquidErrors }) => {
1176
+ this.setState(
1177
+ (prevState) => ({
1178
+ liquidErrorMessage: {
1179
+ ...prevState.liquidErrorMessage,
1180
+ STANDARD_ERROR_MSG: standardErrors,
1181
+ LIQUID_ERROR_MSG: liquidErrors,
1182
+ },
1183
+ }),
1184
+ () => {
1185
+ this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage, this.state.currentTab);
1186
+ this.props.stopValidation();
1187
+ }
1188
+ );
1189
+ };
1190
+
1191
+ const onSuccess = (contentToSubmit) => {
1192
+ // Clear any previous errors
1193
+ this.setState(
1194
+ (prevState) => ({
1195
+ liquidErrorMessage: {
1196
+ ...prevState.liquidErrorMessage,
1197
+ STANDARD_ERROR_MSG: [],
1198
+ LIQUID_ERROR_MSG: [],
1199
+ },
1200
+ }),
1201
+ () => {
1202
+ // Format depends on the tabType
1203
+ this.handleLiquidTemplateSubmit(contentToSubmit);
1204
+ }
1205
+ );
1206
+ };
1207
+
1208
+ // Call the utility function with our callbacks
1209
+ validateMobilePushContent(formData, {
1210
+ currentTab,
1211
+ onError: onLiquidError,
1212
+ onSuccess,
1213
+ getLiquidTags: this.props.actions.getLiquidTags,
1214
+ formatMessage: this.props.intl.formatMessage,
1215
+ messages: globalMessages,
1216
+ tagLookupMap: _.get(this.props, 'tagStore.tagLookupMap', {}),
1217
+ eventContextTags: _.get(this.props, 'tagStore.eventContextTags', []),
1218
+ isLiquidFlow: this.isLiquidFlowSupported(), // Use the method instead of props
1219
+ forwardedTags: _.get(this.props, 'tagStore.forwardedTags', {}),
1220
+ skipTags: this.skipTags.bind(this),
1221
+ extractNames,
1222
+ checkSupport
1223
+ });
1224
+ }
1225
+
1169
1226
  transformInjectedTags(tags) {
1170
1227
  _.forEach(tags, (tag) => {
1171
1228
  const temp = tag;
@@ -1304,7 +1361,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
1304
1361
  });
1305
1362
 
1306
1363
  ifSupported = ifSupported || this.checkIfSupportedTag(tagValue, injectedTags);
1307
- if (!ifSupported && !this.liquidFlow) {
1364
+ if (!ifSupported && !this.liquidFlow()) {
1308
1365
  response.unsupportedTags.push(tagValue);
1309
1366
  response.valid = false;
1310
1367
  }
@@ -2278,12 +2335,11 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
2278
2335
  };
2279
2336
  handleOk = (ev) => {
2280
2337
  const id = ev.target.id;
2281
-
2282
2338
  this.setState({showModal: false});
2283
2339
  if (id === "android" && this.state.androidValid) {
2284
- this.props.onSubmit(this.state.formData);
2340
+ this.onSubmitWrapper();
2285
2341
  } else if (id === "ios" && this.state.iosValid) {
2286
- this.props.onSubmit(this.state.formData);
2342
+ this.onSubmitWrapper();
2287
2343
  } else if (id === "sms-version-modal") {
2288
2344
  this.state.currentEvent.call(this.props.parent, this.state.currentEventData, this.state.currentTab);
2289
2345
  this.setState({currentEvent: {}, currentEventData: {}});
@@ -2483,6 +2539,19 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
2483
2539
  default:
2484
2540
  break;
2485
2541
  }
2542
+ const prevErrorMessage = this.state.liquidErrorMessage?.STANDARD_ERROR_MSG?.[0];
2543
+ if (prevErrorMessage !== errorMessageText && errorMessageText && this.liquidFlow()) {
2544
+ this.setState((prevState) => ({
2545
+ liquidErrorMessage: {
2546
+ ...prevState.liquidErrorMessage,
2547
+ STANDARD_ERROR_MSG: [errorMessageText],
2548
+ }
2549
+ }), () => {
2550
+
2551
+ this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage, this.props.channel === SMS? null: this.state.currentTab);
2552
+ });
2553
+ }
2554
+ else{
2486
2555
  if (styling === 'semantic') {
2487
2556
  columns.push(
2488
2557
  <CapColumn key="input" span={val.width} offset={offset}>
@@ -2490,7 +2559,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
2490
2559
  id={val.id}
2491
2560
  placeholder={val.placeholder ? val.placeholder : ''}
2492
2561
  className={`${ifError ? 'error-form-builder' : ''}`}
2493
- errorMessage={errorMessageText}
2562
+ errorMessage={errorMessageText && !this.liquidFlow() ? errorMessageText : ''}
2494
2563
  label={val.label}
2495
2564
  autosize={val.autosize ? val.autosizeParams : false}
2496
2565
  onChange={(e) => this.updateFormData(e.target.value, val)}
@@ -2520,8 +2589,10 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
2520
2589
  </CapColumn>
2521
2590
  );
2522
2591
  }
2592
+ }
2523
2593
  };
2524
2594
 
2595
+
2525
2596
  renderColLabelSection(section, childIndex) {
2526
2597
  // const schema = this.props.schema
2527
2598
  const fields = [];
@@ -3465,7 +3536,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
3465
3536
  <CapColumn
3466
3537
  style={val.colStyle ? val.colStyle : {border : ""}}
3467
3538
  span={val.width}
3468
- className={`${(this.state.liquidErrorMessage?.LIQUID_ERROR_MSG?.length || this.state.liquidErrorMessage?.STANDARD_ERROR_MSG?.length) && this.liquidFlow && "error-boundary"} `}
3539
+ className={`${(this.state.liquidErrorMessage?.LIQUID_ERROR_MSG?.length || this.state.liquidErrorMessage?.STANDARD_ERROR_MSG?.length) && this.liquidFlow() && "error-boundary"} `}
3469
3540
  >
3470
3541
  <CKEditor
3471
3542
  id={val.id}
@@ -3509,7 +3580,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
3509
3580
  isModuleFilterEnabled = this.props.isFullMode;
3510
3581
  }
3511
3582
  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"}`}>
3583
+ <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
3584
  <BeeEditor
3514
3585
  uid={uuid}
3515
3586
  tokenData={beeToken}
@@ -3877,7 +3948,8 @@ FormBuilder.propTypes = {
3877
3948
  isFullMode: PropTypes.bool,
3878
3949
  currentOrgDetails: PropTypes.object,
3879
3950
  liquidExtractionInProgress: PropTypes.bool,
3880
- showLiquidErrorInFooter: PropTypes.func
3951
+ showLiquidErrorInFooter: PropTypes.func,
3952
+ channel: PropTypes.string,
3881
3953
  };
3882
3954
 
3883
3955
  const mapStateToProps = createStructuredSelector({
@@ -3894,3 +3966,4 @@ function mapDispatchToProps(dispatch) {
3894
3966
  }
3895
3967
 
3896
3968
  export default connect(mapStateToProps,mapDispatchToProps)(injectIntl(FormBuilder));
3969
+
@@ -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
  },
@@ -26,7 +26,7 @@ const initializeComponent = () => {
26
26
  describe('renders a MarketingObjective', () => {
27
27
  it("Test if MarketingObjective component renders", () => {
28
28
  initializeComponent();
29
- const button = screen.getByRole('button', {name: 'Done'});
29
+ const button = screen.getByText('Done');
30
30
  expect(button).toBeInTheDocument();
31
31
  fireEvent.click(button);
32
32
  });
@@ -5,11 +5,10 @@ import CapCollapsibleLeftNavigationSagas from '@capillarytech/cap-ui-library/Cap
5
5
  import { analyticsBotSaga } from "@capillarytech/cap-ui-library/CapAskAira";
6
6
 
7
7
  describe("analyticsBotSagaFn", () => {
8
- it("should call all analytics bot sagas", () => {
8
+ test.concurrent("should call all analytics bot sagas", () => {
9
9
  const { saga: analyticsBotSagaFn } = rootSagas.find(
10
10
  s => s.key === "analyticsBotSaga"
11
11
  );
12
-
13
12
  return expectSaga(analyticsBotSagaFn)
14
13
  .provide([
15
14
  [matchers.call.fn(analyticsBotSaga[0]), undefined],
@@ -20,7 +19,7 @@ describe("analyticsBotSagaFn", () => {
20
19
  });
21
20
 
22
21
  describe("navigationConfigSaga", () => {
23
- it("should call all analytics bot sagas", () => {
22
+ it.concurrent("should call all analytics bot sagas", () => {
24
23
  const { saga: navigationConfigSaga } = rootSagas.find(
25
24
  s => s.key === "navigation"
26
25
  );
@@ -1,6 +1,6 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
- exports[`galleryReducer Initial State should match snapshot returns the initial state 1`] = `
3
+ exports[` 1`] = `
4
4
  Immutable.Map {
5
5
  "assetList": Immutable.List [],
6
6
  "assetUploading": false,
@@ -1,10 +1,9 @@
1
-
2
1
  import * as actions from '../actions';
3
2
  import * as types from '../constants';
4
3
 
5
4
  describe('Gallery actions', () => {
6
5
  describe('uploadAsset', () => {
7
- it('has a type of UPLOAD_ASSET_REQUEST', () => {
6
+ test.concurrent('has a type of UPLOAD_ASSET_REQUEST', () => {
8
7
  const expected = {
9
8
  type: types.UPLOAD_ASSET_REQUEST,
10
9
  file: 'test',
@@ -15,7 +14,7 @@ describe('Gallery actions', () => {
15
14
  });
16
15
 
17
16
  describe('getAllAssets', () => {
18
- it('has a type of GET_ALL_ASSETS_REQUEST', () => {
17
+ test.concurrent('has a type of GET_ALL_ASSETS_REQUEST', () => {
19
18
  const expected = {
20
19
  type: types.GET_ALL_ASSETS_REQUEST,
21
20
  assetType: 'IMAGE',
@@ -6,7 +6,7 @@ import * as actions from '../actions';
6
6
 
7
7
  describe('galleryReducer', () => {
8
8
  describe('Initial State should match snapshot', () => {
9
- it('returns the initial state', () => {
9
+ it.concurrent('returns the initial state', () => {
10
10
  expect(galleryReducer(undefined, {})).toMatchSnapshot();
11
11
  });
12
12
  });
@@ -50,14 +50,14 @@ describe('galleryReducer', () => {
50
50
  error: 'Some error Occurred',
51
51
  };
52
52
  const stateOnFailure = galleryReducer(initialState, failureAction);
53
- it('should set fetching_schedule_message to true on request', () => {
53
+ it.concurrent('should set fetching_schedule_message to true on request', () => {
54
54
  expect(stateOnRequest.toJS()).toEqual({ assetList: [], fetchingAllAssets: true, fetchAllAssetSuccess: false });
55
55
  });
56
56
 
57
- it('should set assetList from response on success', () => {
57
+ it.concurrent('should set assetList from response on success', () => {
58
58
  expect(stateOnSuccess.toJS()).toEqual(successStateExpected);
59
59
  });
60
- it('should set fetching_schedule_message to false on GET_ALL_ASSETS_FAILURE', () => {
60
+ it.concurrent('should set fetching_schedule_message to false on GET_ALL_ASSETS_FAILURE', () => {
61
61
  expect(stateOnFailure.toJS()).toEqual({assetList: [], fetchingAllAssets: false, fetchAllAssetSuccess: false});
62
62
  });
63
63
  });
@@ -82,14 +82,14 @@ describe('galleryReducer', () => {
82
82
  error: 'Some error Occurred',
83
83
  };
84
84
  const stateOnFailure = galleryReducer(initialState, failureAction);
85
- it('should set assetUploading to true and uploadAssetSuccess to false on request', () => {
85
+ it.concurrent('should set assetUploading to true and uploadAssetSuccess to false on request', () => {
86
86
  expect(stateOnRequest.toJS()).toEqual({ uploadAssetSuccess: false, assetUploading: true });
87
87
  });
88
88
 
89
- it('should set assetUploading to false and uploadAssetSuccess to true on success', () => {
89
+ it.concurrent('should set assetUploading to false and uploadAssetSuccess to true on success', () => {
90
90
  expect(stateOnSuccess.toJS()).toEqual(successStateExpected);
91
91
  });
92
- it('should set assetUploading to false and uploadAssetSuccess to false on UPLOAD_ASSET_FAILURE', () => {
92
+ it.concurrent('should set assetUploading to false and uploadAssetSuccess to false on UPLOAD_ASSET_FAILURE', () => {
93
93
  expect(stateOnFailure.toJS()).toEqual({ uploadAssetSuccess: false, assetUploading: false });
94
94
  });
95
95
  });