@capillarytech/creatives-library 8.0.292-alpha.1 → 8.0.292-alpha.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/constants/unified.js +1 -3
- package/initialState.js +2 -0
- package/package.json +1 -1
- package/utils/common.js +8 -5
- package/utils/commonUtils.js +85 -4
- package/utils/tagValidations.js +223 -83
- package/utils/tests/commonUtil.test.js +124 -147
- package/utils/tests/tagValidations.test.js +358 -441
- package/v2Components/ErrorInfoNote/index.js +5 -2
- package/v2Components/FormBuilder/index.js +203 -137
- package/v2Components/FormBuilder/messages.js +8 -0
- package/v2Components/HtmlEditor/HTMLEditor.js +11 -2
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -0
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +15 -0
- package/v2Components/HtmlEditor/_htmlEditor.scss +5 -1
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +10 -10
- package/v2Components/HtmlEditor/components/DeviceToggle/FLOW_AND_CLICK_BEHAVIOUR.md +70 -0
- package/v2Components/HtmlEditor/hooks/useInAppContent.js +15 -8
- package/v2Containers/Cap/mockData.js +14 -0
- package/v2Containers/Cap/reducer.js +55 -3
- package/v2Containers/Cap/tests/reducer.test.js +102 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +2 -5
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +5 -13
- package/v2Containers/CreativesContainer/index.js +10 -47
- package/v2Containers/Email/index.js +5 -1
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +70 -23
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +137 -29
- package/v2Containers/FTP/index.js +51 -2
- package/v2Containers/FTP/messages.js +4 -0
- package/v2Containers/InApp/index.js +142 -51
- package/v2Containers/InApp/tests/index.test.js +6 -17
- package/v2Containers/InappAdvance/index.js +120 -10
- package/v2Containers/InappAdvance/tests/index.test.js +2 -3
- package/v2Containers/Line/Container/Text/index.js +1 -0
- package/v2Containers/MobilePush/Create/index.js +19 -45
- package/v2Containers/MobilePush/Edit/index.js +20 -47
- package/v2Containers/MobilePushNew/index.js +32 -12
- package/v2Containers/MobilepushWrapper/index.js +1 -3
- package/v2Containers/Rcs/index.js +37 -12
- package/v2Containers/Sms/Create/index.js +3 -39
- package/v2Containers/Sms/Create/messages.js +0 -4
- package/v2Containers/Sms/Edit/index.js +3 -35
- package/v2Containers/Sms/commonMethods.js +6 -3
- package/v2Containers/SmsTrai/Edit/index.js +47 -11
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +6 -6
- package/v2Containers/SmsWrapper/index.js +0 -2
- package/v2Containers/Viber/index.js +1 -0
- package/v2Containers/WebPush/Create/hooks/useTagManagement.js +3 -1
- package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +7 -0
- package/v2Containers/WebPush/Create/index.js +2 -2
- package/v2Containers/WebPush/Create/utils/validation.js +8 -17
- package/v2Containers/WebPush/Create/utils/validation.test.js +24 -44
- package/v2Containers/Whatsapp/index.js +17 -9
- package/v2Containers/Zalo/index.js +11 -3
- package/v2Containers/Sms/tests/commonMethods.test.js +0 -122
|
@@ -38,8 +38,7 @@ import v2MobilePushCreateReducer from './reducer';
|
|
|
38
38
|
import { v2MobilePushWatchDuplicateTemplateSaga } from './sagas';
|
|
39
39
|
import { EXTERNAL_LINK_LOWERCASE } from './constants';
|
|
40
40
|
import TestAndPreviewSlidebox from '../../../v2Components/TestAndPreviewSlidebox';
|
|
41
|
-
import { checkForPersonalizationTokens
|
|
42
|
-
import formBuilderMessages from '../../../v2Components/FormBuilder/messages';
|
|
41
|
+
import { checkForPersonalizationTokens } from '../../../utils/commonUtils';
|
|
43
42
|
|
|
44
43
|
const PrefixWrapper = styled.div`
|
|
45
44
|
margin-right: 16px;
|
|
@@ -95,42 +94,8 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
95
94
|
this.props.globalActions.fetchSchemaForEntity(query);
|
|
96
95
|
};
|
|
97
96
|
componentWillReceiveProps = (nextProps) => {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
// If form already has validation errors (braces, personalization tags, etc.), do not call extractTags; just fail
|
|
101
|
-
if (!this.state.isFormValid && nextProps.onValidationFail) {
|
|
102
|
-
nextProps.onValidationFail();
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
if (nextProps.getLiquidTags && nextProps.showLiquidErrorInFooter && nextProps.onValidationFail) {
|
|
106
|
-
const formDataArr = [this.state.formData?.[0], this.state.formData?.[1]];
|
|
107
|
-
validateMobilePushContent(formDataArr, {
|
|
108
|
-
currentTab: this.state.currentTab,
|
|
109
|
-
getLiquidTags: nextProps.getLiquidTags,
|
|
110
|
-
formatMessage: this.props.intl.formatMessage,
|
|
111
|
-
messages: formBuilderMessages,
|
|
112
|
-
onError: (err) => {
|
|
113
|
-
const { standardErrors = [], liquidErrors = [] } = err;
|
|
114
|
-
// _validatePlatformSpecificContent passes { standardErrors: { ANDROID, IOS, generic }, liquidErrors: { ... } }; footer expects arrays
|
|
115
|
-
const toArray = (v) => (Array.isArray(v) ? v : (v && typeof v === 'object' ? [].concat(...Object.values(v)) : []));
|
|
116
|
-
const STANDARD_ERROR_MSG = toArray(standardErrors);
|
|
117
|
-
const LIQUID_ERROR_MSG = toArray(liquidErrors);
|
|
118
|
-
nextProps.showLiquidErrorInFooter(
|
|
119
|
-
{ STANDARD_ERROR_MSG, LIQUID_ERROR_MSG },
|
|
120
|
-
this.state.currentTab
|
|
121
|
-
);
|
|
122
|
-
// Only trigger onValidationFail when there are actual errors; skip when helper called onError with empty arrays (reset case)
|
|
123
|
-
if (STANDARD_ERROR_MSG.length > 0 || LIQUID_ERROR_MSG.length > 0) {
|
|
124
|
-
nextProps.onValidationFail();
|
|
125
|
-
}
|
|
126
|
-
},
|
|
127
|
-
onSuccess: () => {
|
|
128
|
-
nextProps.getFormLibraryData(this.getFormData());
|
|
129
|
-
},
|
|
130
|
-
});
|
|
131
|
-
} else {
|
|
132
|
-
nextProps.getFormLibraryData(this.getFormData());
|
|
133
|
-
}
|
|
97
|
+
if (nextProps.isGetFormData && !this.props.isFullMode) {
|
|
98
|
+
nextProps.getFormLibraryData(this.getFormData());
|
|
134
99
|
} else if (nextProps.isGetFormData && this.props.isGetFormData !== nextProps.isGetFormData && this.props.isFullMode && !this.props.Create.createTemplateInProgress) {
|
|
135
100
|
this.startValidation();
|
|
136
101
|
}
|
|
@@ -713,21 +678,31 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
713
678
|
}
|
|
714
679
|
showError = () => {
|
|
715
680
|
const {intl} = this.props;
|
|
716
|
-
const {errorData
|
|
681
|
+
const {errorData} = this.state;
|
|
717
682
|
const errorMessage = {key: 'validation-error', message: intl.formatMessage(messages.validationError)};
|
|
718
683
|
if (!isEmpty(this.state.formData) && !this.state.isFormValid) {
|
|
719
684
|
let tab = this.state.currentTab;
|
|
720
|
-
const isAndroidInvalid = Object.values(errorData[0]
|
|
721
|
-
const isIosInvalid = Object.values(errorData[1]
|
|
722
|
-
|
|
685
|
+
const isAndroidInvalid = Object.values(errorData[0]).includes(true);
|
|
686
|
+
const isIosInvalid = Object.values(errorData[1]).includes(true);
|
|
687
|
+
let isTagErrorExist = false;
|
|
723
688
|
if (isAndroidInvalid) {
|
|
724
689
|
tab = 1;
|
|
725
690
|
errorMessage.description = intl.formatMessage(messages.invalidAndroidMessage);
|
|
726
|
-
|
|
691
|
+
const invalidTags = errorData[0]['invalid-tags'];
|
|
692
|
+
if (!isEmpty(invalidTags)) {
|
|
693
|
+
isTagErrorExist = true;
|
|
694
|
+
errorMessage.description = `${intl.formatMessage(messages.invalidAndroidMessage)} ${intl.formatMessage(messages.invalidTags)}: ${invalidTags.join(',')} `;
|
|
695
|
+
}
|
|
696
|
+
} else if (isIosInvalid) {
|
|
727
697
|
tab = 2;
|
|
728
698
|
errorMessage.description = intl.formatMessage(messages.invalidIosMessage);
|
|
699
|
+
const invalidTags = errorData[1]['invalid-tags'];
|
|
700
|
+
if (!isEmpty(invalidTags)) {
|
|
701
|
+
isTagErrorExist = true;
|
|
702
|
+
errorMessage.description = `${intl.formatMessage(messages.invalidIosMessage)} ${intl.formatMessage(messages.invalidTags)}: ${invalidTags.join(',')} `;
|
|
703
|
+
}
|
|
729
704
|
}
|
|
730
|
-
if (tab !== this.state.currentTab) {
|
|
705
|
+
if (tab !== this.state.currentTab || isTagErrorExist) {
|
|
731
706
|
CapNotification.error(errorMessage);
|
|
732
707
|
}
|
|
733
708
|
}
|
|
@@ -2054,7 +2029,6 @@ Create.propTypes = {
|
|
|
2054
2029
|
onPreviewContentClicked: PropTypes.func,
|
|
2055
2030
|
onTestContentClicked: PropTypes.func,
|
|
2056
2031
|
eventContextTags: PropTypes.array,
|
|
2057
|
-
getLiquidTags: PropTypes.func,
|
|
2058
2032
|
showLiquidErrorInFooter: PropTypes.func,
|
|
2059
2033
|
showTestAndPreviewSlidebox: PropTypes.bool,
|
|
2060
2034
|
handleTestAndPreview: PropTypes.func,
|
|
@@ -39,8 +39,7 @@ import { v2MobilePushEditSagas } from './sagas';
|
|
|
39
39
|
import v2MobilePushEditReducer from './reducer';
|
|
40
40
|
import * as globalActions from '../../Cap/actions';
|
|
41
41
|
import { MAPP_SDK } from './constants';
|
|
42
|
-
import { checkForPersonalizationTokens, isEmbeddedEditOrPreview
|
|
43
|
-
import formBuilderMessages from '../../../v2Components/FormBuilder/messages';
|
|
42
|
+
import { checkForPersonalizationTokens, isEmbeddedEditOrPreview } from '../../../utils/commonUtils';
|
|
44
43
|
import { EMBEDDED } from '../../Whatsapp/constants';
|
|
45
44
|
import { OUTBOUND } from '../../../v2Components/FormBuilder/constants';
|
|
46
45
|
import TestAndPreviewSlidebox from '../../../v2Components/TestAndPreviewSlidebox';
|
|
@@ -130,43 +129,8 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
|
|
|
130
129
|
this.hasFetchedInitialTagsRef = false;
|
|
131
130
|
this.lastFetchedTagContextRef = null;
|
|
132
131
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
// If form already has validation errors (braces, personalization tags, etc.), do not call extractTags; just fail
|
|
136
|
-
if (!this.state.isFormValid && nextProps.onValidationFail) {
|
|
137
|
-
nextProps.onValidationFail();
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
if (nextProps.getLiquidTags && nextProps.showLiquidErrorInFooter && nextProps.onValidationFail) {
|
|
141
|
-
const formDataArr = [this.state.formData?.[0], this.state.formData?.[1]];
|
|
142
|
-
validateMobilePushContent(formDataArr, {
|
|
143
|
-
currentTab: this.state.currentTab,
|
|
144
|
-
getLiquidTags: nextProps.getLiquidTags,
|
|
145
|
-
formatMessage: this.props.intl.formatMessage,
|
|
146
|
-
messages: formBuilderMessages,
|
|
147
|
-
onError: (err) => {
|
|
148
|
-
const { standardErrors = [], liquidErrors = [] } = err;
|
|
149
|
-
// _validatePlatformSpecificContent passes { standardErrors: { ANDROID, IOS, generic }, liquidErrors: { ... } }; footer expects arrays
|
|
150
|
-
const toArray = (v) => (Array.isArray(v) ? v : (v && typeof v === 'object' ? [].concat(...Object.values(v)) : []));
|
|
151
|
-
const STANDARD_ERROR_MSG = toArray(standardErrors);
|
|
152
|
-
const LIQUID_ERROR_MSG = toArray(liquidErrors);
|
|
153
|
-
nextProps.showLiquidErrorInFooter(
|
|
154
|
-
{ STANDARD_ERROR_MSG, LIQUID_ERROR_MSG },
|
|
155
|
-
this.state.currentTab
|
|
156
|
-
);
|
|
157
|
-
// Only treat as validation failure when there are actual errors; skip when helper called onError with empty reset payload
|
|
158
|
-
const hasErrors = STANDARD_ERROR_MSG.length > 0 || LIQUID_ERROR_MSG.length > 0;
|
|
159
|
-
if (hasErrors && nextProps.onValidationFail) {
|
|
160
|
-
nextProps.onValidationFail();
|
|
161
|
-
}
|
|
162
|
-
},
|
|
163
|
-
onSuccess: () => {
|
|
164
|
-
nextProps.getFormLibraryData(this.getFormData());
|
|
165
|
-
},
|
|
166
|
-
});
|
|
167
|
-
} else {
|
|
168
|
-
nextProps.getFormLibraryData(this.getFormData());
|
|
169
|
-
}
|
|
132
|
+
if (nextProps.isGetFormData && !this.props.isFullMode) {
|
|
133
|
+
nextProps.getFormLibraryData(this.getFormData());
|
|
170
134
|
} else if (nextProps.isGetFormData && this.props.isGetFormData !== nextProps.isGetFormData && this.props.isFullMode && !this.props.Create.createTemplateInProgress) {
|
|
171
135
|
this.startValidation();
|
|
172
136
|
}
|
|
@@ -741,22 +705,32 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
|
|
|
741
705
|
// eslint-disable-next-line react/sort-comp
|
|
742
706
|
showError = () => {
|
|
743
707
|
const {intl} = this.props;
|
|
744
|
-
const {errorData
|
|
708
|
+
const {errorData} = this.state;
|
|
745
709
|
const errorMessage = {key: 'validation-error', message: intl.formatMessage(messages.validationError)};
|
|
710
|
+
let isTagErrorExist = false;
|
|
746
711
|
if (!_.isEmpty(this.state.formData) && !this.state.isFormValid) {
|
|
747
712
|
let tab = this.state.currentTab;
|
|
748
|
-
const isAndroidInvalid = Object.values(errorData[0]
|
|
749
|
-
const isIosInvalid = Object.values(errorData[1]
|
|
750
|
-
const isIosTabVisible = get(schema, 'containers[0].panes[1].isSupported', true) !== false;
|
|
713
|
+
const isAndroidInvalid = Object.values(errorData[0]).includes(true);
|
|
714
|
+
const isIosInvalid = Object.values(errorData[1]).includes(true);
|
|
751
715
|
if (isAndroidInvalid) {
|
|
752
716
|
tab = 1;
|
|
753
717
|
errorMessage.description = intl.formatMessage(messages.invalidAndroidMessage);
|
|
754
|
-
|
|
718
|
+
const invalidTags = errorData[0]['invalid-tags'];
|
|
719
|
+
if (!_.isEmpty(invalidTags)) {
|
|
720
|
+
isTagErrorExist = true;
|
|
721
|
+
errorMessage.description = `${intl.formatMessage(messages.invalidAndroidMessage)} ${intl.formatMessage(messages.invalidTags)}: ${invalidTags.join(',')} `;
|
|
722
|
+
}
|
|
723
|
+
} else if (isIosInvalid) {
|
|
755
724
|
tab = 2;
|
|
756
725
|
errorMessage.description = intl.formatMessage(messages.invalidIosMessage);
|
|
726
|
+
const invalidTags = errorData[1]['invalid-tags'];
|
|
727
|
+
if (!_.isEmpty(invalidTags)) {
|
|
728
|
+
isTagErrorExist = true;
|
|
729
|
+
errorMessage.description = `${intl.formatMessage(messages.invalidIosMessage)} ${intl.formatMessage(messages.invalidTags)}: ${invalidTags.join(',')} `;
|
|
730
|
+
}
|
|
757
731
|
}
|
|
758
732
|
|
|
759
|
-
if (tab !== this.state.currentTab) {
|
|
733
|
+
if (tab !== this.state.currentTab || isTagErrorExist) {
|
|
760
734
|
CapNotification.error(errorMessage);
|
|
761
735
|
}
|
|
762
736
|
}
|
|
@@ -2333,12 +2307,11 @@ Edit.propTypes = {
|
|
|
2333
2307
|
getFormLibraryData: PropTypes.func,
|
|
2334
2308
|
isGetFormData: PropTypes.bool,
|
|
2335
2309
|
Create: PropTypes.object,
|
|
2336
|
-
onValidationFail: PropTypes.
|
|
2310
|
+
onValidationFail: PropTypes.bool,
|
|
2337
2311
|
onPreviewContentClicked: PropTypes.func,
|
|
2338
2312
|
onTestContentClicked: PropTypes.func,
|
|
2339
2313
|
creativesMode: PropTypes.string,
|
|
2340
2314
|
eventContextTags: PropTypes.array,
|
|
2341
|
-
getLiquidTags: PropTypes.func,
|
|
2342
2315
|
showLiquidErrorInFooter: PropTypes.func,
|
|
2343
2316
|
showTestAndPreviewSlidebox: PropTypes.bool,
|
|
2344
2317
|
handleTestAndPreview: PropTypes.func,
|
|
@@ -77,6 +77,7 @@ import { getContent } from "../MobilePush/commonMethods";
|
|
|
77
77
|
import { getMessageObject } from "../../utils/messageUtils";
|
|
78
78
|
import { gtmPush } from "../../utils/gtmTrackers";
|
|
79
79
|
import mobilePushReducer from "./reducer";
|
|
80
|
+
import { hasLiquidSupportFeature } from "../../utils/common";
|
|
80
81
|
import formBuilderMessages from "../../v2Components/FormBuilder/messages";
|
|
81
82
|
import { validateMobilePushContent } from "../../utils/commonUtils";
|
|
82
83
|
import { getSingleTab } from "../InApp/utils";
|
|
@@ -802,9 +803,10 @@ const MobilePushNew = ({
|
|
|
802
803
|
(value) => {
|
|
803
804
|
let errorTemplateDescMessage = "";
|
|
804
805
|
|
|
805
|
-
const { isBraceError } = validateTags({
|
|
806
|
+
const { unsupportedTags, isBraceError } = validateTags({
|
|
806
807
|
content: value,
|
|
807
808
|
tagsParam: tags,
|
|
809
|
+
injectedTagsParams: injectedTags,
|
|
808
810
|
location,
|
|
809
811
|
tagModule: getDefaultTags,
|
|
810
812
|
isFullMode,
|
|
@@ -814,6 +816,14 @@ const MobilePushNew = ({
|
|
|
814
816
|
messages.emptyTemplateDescErrorMessage
|
|
815
817
|
);
|
|
816
818
|
}
|
|
819
|
+
if (unsupportedTags?.length > 0) {
|
|
820
|
+
errorTemplateDescMessage = formatMessage(
|
|
821
|
+
globalMessages.unsupportedTagsValidationError,
|
|
822
|
+
{
|
|
823
|
+
unsupportedTags,
|
|
824
|
+
}
|
|
825
|
+
);
|
|
826
|
+
}
|
|
817
827
|
if (isBraceError) {
|
|
818
828
|
errorTemplateDescMessage = formatMessage(
|
|
819
829
|
globalMessages.unbalanacedCurlyBraces
|
|
@@ -2649,6 +2659,20 @@ const MobilePushNew = ({
|
|
|
2649
2659
|
getLiquidTags: globalActionsProps.getLiquidTags,
|
|
2650
2660
|
formatMessage,
|
|
2651
2661
|
messages: formBuilderMessages,
|
|
2662
|
+
tagLookupMap: metaEntities?.tagLookupMap || {},
|
|
2663
|
+
eventContextTags: metaEntities?.eventContextTags || [],
|
|
2664
|
+
isLiquidFlow: hasLiquidSupportFeature(),
|
|
2665
|
+
forwardedTags: {},
|
|
2666
|
+
skipTags: (tag) => {
|
|
2667
|
+
const skipRegexes = [
|
|
2668
|
+
/dynamic_expiry_date_after_\d+_days\.FORMAT_\d/,
|
|
2669
|
+
/unsubscribe\(#[a-zA-Z\d]{6}\)/,
|
|
2670
|
+
/Link_to_[a-zA-z]/,
|
|
2671
|
+
/SURVEY.*\.TOKEN/,
|
|
2672
|
+
/^[A-Za-z].*\([a-zA-Z\d]*\)/,
|
|
2673
|
+
];
|
|
2674
|
+
return skipRegexes.some((regex) => regex.test(tag));
|
|
2675
|
+
},
|
|
2652
2676
|
singleTab: getSingleTab(accountData),
|
|
2653
2677
|
});
|
|
2654
2678
|
}, [
|
|
@@ -2657,9 +2681,12 @@ const MobilePushNew = ({
|
|
|
2657
2681
|
activeTab,
|
|
2658
2682
|
globalActionsProps,
|
|
2659
2683
|
formatMessage,
|
|
2684
|
+
metaEntities,
|
|
2660
2685
|
accountData,
|
|
2661
2686
|
]);
|
|
2662
2687
|
|
|
2688
|
+
const isLiquidFlow = hasLiquidSupportFeature();
|
|
2689
|
+
|
|
2663
2690
|
useEffect(() => {
|
|
2664
2691
|
// Always map to { label } for both platforms
|
|
2665
2692
|
const newButtons = Array.isArray(ctaData)
|
|
@@ -2906,22 +2933,16 @@ const MobilePushNew = ({
|
|
|
2906
2933
|
setShowTestAndPreviewSlidebox(false);
|
|
2907
2934
|
}, []);
|
|
2908
2935
|
|
|
2909
|
-
// Add useEffect to handle isGetFormData prop changes
|
|
2910
|
-
// In library mode: run liquid validation (extractTags) first; on success liquidMiddleWare calls handleSave
|
|
2911
|
-
// In full mode: call handleSave directly
|
|
2936
|
+
// Add useEffect to handle isGetFormData prop changes
|
|
2912
2937
|
useEffect(() => {
|
|
2913
2938
|
if (isGetFormData) {
|
|
2914
|
-
|
|
2915
|
-
liquidMiddleWare();
|
|
2916
|
-
} else {
|
|
2917
|
-
handleSave();
|
|
2918
|
-
}
|
|
2939
|
+
handleSave();
|
|
2919
2940
|
// Reset the flag to prevent infinite loop
|
|
2920
2941
|
if (onValidationFail) {
|
|
2921
2942
|
onValidationFail();
|
|
2922
2943
|
}
|
|
2923
2944
|
}
|
|
2924
|
-
}, [isGetFormData, handleSave, onValidationFail
|
|
2945
|
+
}, [isGetFormData, handleSave, onValidationFail]);
|
|
2925
2946
|
|
|
2926
2947
|
// Add message event listener to handle parent communication (like old MobilePush components)
|
|
2927
2948
|
useEffect(() => {
|
|
@@ -3062,8 +3083,7 @@ const MobilePushNew = ({
|
|
|
3062
3083
|
<CapButton
|
|
3063
3084
|
type="primary"
|
|
3064
3085
|
onClick={() => {
|
|
3065
|
-
|
|
3066
|
-
if (!isFullMode) {
|
|
3086
|
+
if (isLiquidFlow) {
|
|
3067
3087
|
liquidMiddleWare();
|
|
3068
3088
|
} else {
|
|
3069
3089
|
handleSave();
|
|
@@ -72,7 +72,7 @@ export class MobilepushWrapper extends React.Component { // eslint-disable-line
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
render() {
|
|
75
|
-
const {mobilePushCreateMode, step, getFormData,
|
|
75
|
+
const {mobilePushCreateMode, step, getFormData, setIsLoadingContent, isGetFormData, query, isFullMode, showTemplateName, type, onValidationFail, onPreviewContentClicked, onTestContentClicked, templateData, eventContextTags = [], showTestAndPreviewSlidebox, handleTestAndPreview, handleCloseTestAndPreview, restrictPersonalization, isAnonymousType, onPersonalizationTokensChange} = this.props;
|
|
76
76
|
const {templateName} = this.state;
|
|
77
77
|
const isShowMobilepushCreate = !isEmpty(mobilePushCreateMode);
|
|
78
78
|
return (
|
|
@@ -102,7 +102,6 @@ export class MobilepushWrapper extends React.Component { // eslint-disable-line
|
|
|
102
102
|
<div>
|
|
103
103
|
{isShowMobilepushCreate && <MobilepushCreate
|
|
104
104
|
getFormLibraryData={getFormData}
|
|
105
|
-
getLiquidTags={getLiquidTags}
|
|
106
105
|
setIsLoadingContent={setIsLoadingContent}
|
|
107
106
|
defaultData={{"template-name": templateName}}
|
|
108
107
|
location={{
|
|
@@ -144,7 +143,6 @@ MobilepushWrapper.propTypes = {
|
|
|
144
143
|
mobilePushCreateMode: PropTypes.string,
|
|
145
144
|
step: PropTypes.string,
|
|
146
145
|
getFormData: PropTypes.string,
|
|
147
|
-
getLiquidTags: PropTypes.func,
|
|
148
146
|
setIsLoadingContent: PropTypes.func,
|
|
149
147
|
onEnterTemplateName: PropTypes.func,
|
|
150
148
|
onRemoveTemplateName: PropTypes.func,
|
|
@@ -394,15 +394,23 @@ export const Rcs = (props) => {
|
|
|
394
394
|
const validationResponse =
|
|
395
395
|
validateTags({
|
|
396
396
|
content: contentForValidation,
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
397
|
+
tagsParam: tags,
|
|
398
|
+
injectedTagsParams: injectedTags,
|
|
399
|
+
location,
|
|
400
|
+
tagModule: getDefaultTags,
|
|
401
|
+
eventContextTags,
|
|
402
|
+
isFullMode,
|
|
403
|
+
}) || {};
|
|
404
|
+
const unsupportedTagsLengthCheck =
|
|
405
|
+
validationResponse?.unsupportedTags?.length > 0;
|
|
406
|
+
const errorMsg =
|
|
407
|
+
(unsupportedTagsLengthCheck &&
|
|
408
|
+
formatMessage(globalMessages.unsupportedTagsValidationError, {
|
|
409
|
+
unsupportedTags: validationResponse.unsupportedTags,
|
|
410
|
+
})) ||
|
|
411
|
+
(validationResponse.isBraceError &&
|
|
412
|
+
formatMessage(globalMessages.unbalanacedCurlyBraces)) ||
|
|
413
|
+
false;
|
|
406
414
|
if (type === TITLE_TEXT) setTemplateTitleError(errorMsg);
|
|
407
415
|
if (type === MESSAGE_TEXT) setTemplateDescError(errorMsg);
|
|
408
416
|
};
|
|
@@ -827,9 +835,10 @@ export const Rcs = (props) => {
|
|
|
827
835
|
|
|
828
836
|
const templateDescErrorHandler = (value) => {
|
|
829
837
|
let errorMessage = false;
|
|
830
|
-
const { isBraceError } = validateTags({
|
|
838
|
+
const { unsupportedTags, isBraceError } = validateTags({
|
|
831
839
|
content: value,
|
|
832
840
|
tagsParam: tags,
|
|
841
|
+
injectedTagsParams: injectedTags,
|
|
833
842
|
location,
|
|
834
843
|
tagModule: getDefaultTags,
|
|
835
844
|
isFullMode,
|
|
@@ -859,10 +868,26 @@ export const Rcs = (props) => {
|
|
|
859
868
|
};
|
|
860
869
|
|
|
861
870
|
const fallbackMessageErrorHandler = (value) => {
|
|
871
|
+
let errorMessage = false;
|
|
872
|
+
const { unsupportedTags } = validateTags({
|
|
873
|
+
content: value,
|
|
874
|
+
tagsParam: tags,
|
|
875
|
+
injectedTagsParams: injectedTags,
|
|
876
|
+
location,
|
|
877
|
+
tagModule: getDefaultTags,
|
|
878
|
+
isFullMode,
|
|
879
|
+
}) || {};
|
|
862
880
|
if (value?.length > FALLBACK_MESSAGE_MAX_LENGTH) {
|
|
863
|
-
|
|
881
|
+
errorMessage = formatMessage(messages.fallbackMsgLenError);
|
|
882
|
+
} else if (unsupportedTags?.length > 0) {
|
|
883
|
+
errorMessage = formatMessage(
|
|
884
|
+
globalMessages.unsupportedTagsValidationError,
|
|
885
|
+
{
|
|
886
|
+
unsupportedTags,
|
|
887
|
+
},
|
|
888
|
+
);
|
|
864
889
|
}
|
|
865
|
-
return
|
|
890
|
+
return errorMessage;
|
|
866
891
|
};
|
|
867
892
|
|
|
868
893
|
// Check for forbidden characters: square brackets [] and single curly braces {}
|
|
@@ -51,7 +51,6 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
51
51
|
modalContent: {title: "Alert", body: "Do you really want to delete this version?", type: 'confirm', id: 'sms-version-modal'},
|
|
52
52
|
showTestAndPreviewSlidebox: false,
|
|
53
53
|
isTestAndPreviewMode: false,
|
|
54
|
-
pendingGetFormData: false,
|
|
55
54
|
};
|
|
56
55
|
this.saveFormData = this.saveFormData.bind(this);
|
|
57
56
|
this.onFormDataChange = this.onFormDataChange.bind(this);
|
|
@@ -141,9 +140,8 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
141
140
|
}
|
|
142
141
|
|
|
143
142
|
componentWillReceiveProps(nextProps) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
this.setState({ startValidation: true, pendingGetFormData: true });
|
|
143
|
+
if (nextProps.location.query.module === 'library' && nextProps.isGetFormData) {
|
|
144
|
+
nextProps.getFormSubscriptionData(this.getFormData());
|
|
147
145
|
} else if (nextProps.isGetFormData && this.props.isFullMode && !this.props.Create.createTemplateInProgress) {
|
|
148
146
|
this.startValidation();
|
|
149
147
|
}
|
|
@@ -173,9 +171,6 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
173
171
|
}
|
|
174
172
|
|
|
175
173
|
componentWillUnmount() {
|
|
176
|
-
if (this.pendingGetFormDataTimeout) {
|
|
177
|
-
clearTimeout(this.pendingGetFormDataTimeout);
|
|
178
|
-
}
|
|
179
174
|
if (this.props.setIsLoadingContent) {
|
|
180
175
|
this.props.setIsLoadingContent(true); // setting isLoading of CreativesContainer so that slidebox foot can be hidden till content is loaded
|
|
181
176
|
}
|
|
@@ -204,10 +199,6 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
204
199
|
if (currentTab) {
|
|
205
200
|
this.setState({currentTab});
|
|
206
201
|
}
|
|
207
|
-
// Clear footer validation errors on input change so they refresh on next validation
|
|
208
|
-
if (this.props.showLiquidErrorInFooter) {
|
|
209
|
-
this.props.showLiquidErrorInFooter({ STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] });
|
|
210
|
-
}
|
|
211
202
|
}
|
|
212
203
|
|
|
213
204
|
onTagSelect(data, currentTab) {
|
|
@@ -358,25 +349,7 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
358
349
|
}
|
|
359
350
|
|
|
360
351
|
setFormValidity(isFormValid, errorData) {
|
|
361
|
-
this.setState({
|
|
362
|
-
if (this.state.pendingGetFormData && !isFormValid) {
|
|
363
|
-
this.setState({ pendingGetFormData: false, startValidation: false });
|
|
364
|
-
// Reset parent's Done state so next Done click is a fresh attempt
|
|
365
|
-
if (this.props.onValidationFail) {
|
|
366
|
-
this.props.onValidationFail();
|
|
367
|
-
}
|
|
368
|
-
return;
|
|
369
|
-
}
|
|
370
|
-
// In library mode with SMS, submit only when FormBuilder calls onSubmit (after liquid validation).
|
|
371
|
-
if (this.state.pendingGetFormData && this.props.getFormSubscriptionData && this.props.isFullMode) {
|
|
372
|
-
if (this.pendingGetFormDataTimeout) {
|
|
373
|
-
clearTimeout(this.pendingGetFormDataTimeout);
|
|
374
|
-
this.pendingGetFormDataTimeout = null;
|
|
375
|
-
}
|
|
376
|
-
this.props.getFormSubscriptionData(this.getFormData());
|
|
377
|
-
this.setState({ pendingGetFormData: false, startValidation: false });
|
|
378
|
-
}
|
|
379
|
-
});
|
|
352
|
+
this.setState({isFormValid, errorData});
|
|
380
353
|
}
|
|
381
354
|
|
|
382
355
|
injectMessages(elem) {
|
|
@@ -997,15 +970,6 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
997
970
|
}
|
|
998
971
|
|
|
999
972
|
saveFormData() {
|
|
1000
|
-
// In library mode: FormBuilder calls onSubmit only after liquid validation succeeds.
|
|
1001
|
-
// Submit to parent here so the slidebox can close with valid data.
|
|
1002
|
-
if (!this.props.isFullMode) {
|
|
1003
|
-
if (this.state.pendingGetFormData && this.props.getFormSubscriptionData) {
|
|
1004
|
-
this.props.getFormSubscriptionData(this.getFormData());
|
|
1005
|
-
this.setState({ pendingGetFormData: false, startValidation: false });
|
|
1006
|
-
}
|
|
1007
|
-
return;
|
|
1008
|
-
}
|
|
1009
973
|
//Logic to save in db etc
|
|
1010
974
|
const formData = _.cloneDeep(this.state.formData);
|
|
1011
975
|
const obj = {};
|
|
@@ -90,10 +90,6 @@ export default defineMessages({
|
|
|
90
90
|
id: 'creatives.containersV2.Create.validationError',
|
|
91
91
|
defaultMessage: 'Validation error',
|
|
92
92
|
},
|
|
93
|
-
"unbalancedCurlyBraces": {
|
|
94
|
-
id: 'creatives.containersV2.Create.unbalancedCurlyBraces',
|
|
95
|
-
defaultMessage: 'Please close all curly braces in the message.',
|
|
96
|
-
},
|
|
97
93
|
"smsTemplateCreatedSuccess": {
|
|
98
94
|
id: 'creatives.containersV2.Create.smsTemplateCreatedSuccess',
|
|
99
95
|
defaultMessage: 'SMS Template Created Successfully',
|
|
@@ -52,7 +52,6 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
|
|
|
52
52
|
modalContent: {title: "Alert", body: "Do you really want to delete this version?", type: 'confirm', id: 'sms-version-modal'},
|
|
53
53
|
showTestAndPreviewSlidebox: false,
|
|
54
54
|
isTestAndPreviewMode: false,
|
|
55
|
-
pendingGetFormData: false,
|
|
56
55
|
};
|
|
57
56
|
this.saveFormData = this.saveFormData.bind(this);
|
|
58
57
|
this.onFormDataChange = this.onFormDataChange.bind(this);
|
|
@@ -135,8 +134,8 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
|
|
|
135
134
|
}
|
|
136
135
|
|
|
137
136
|
componentWillReceiveProps(nextProps) {
|
|
138
|
-
if (
|
|
139
|
-
this.
|
|
137
|
+
if (nextProps.location.query.module === 'library' && nextProps.isGetFormData) {
|
|
138
|
+
nextProps.getFormSubscriptionData(this.getFormData());
|
|
140
139
|
}
|
|
141
140
|
if ( nextProps.location.query.module === 'library' && nextProps.subscriptionTemplateDetails && nextProps.subscriptionTemplateDetails.name && _.isEmpty(this.state.editData) && !_.isEmpty(this.state.schema)) {
|
|
142
141
|
this.setEditState(nextProps.subscriptionTemplateDetails);
|
|
@@ -190,9 +189,6 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
|
|
|
190
189
|
}
|
|
191
190
|
|
|
192
191
|
componentWillUnmount() {
|
|
193
|
-
if (this.pendingGetFormDataTimeout) {
|
|
194
|
-
clearTimeout(this.pendingGetFormDataTimeout);
|
|
195
|
-
}
|
|
196
192
|
if (this.props.setIsLoadingContent) {
|
|
197
193
|
this.props.setIsLoadingContent(true); // setting isLoading of CreativesContainer so that slidebox foot can be hidden till content is loaded
|
|
198
194
|
}
|
|
@@ -218,10 +214,6 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
|
|
|
218
214
|
if (currentTab) {
|
|
219
215
|
this.setState({currentTab});
|
|
220
216
|
}
|
|
221
|
-
// Clear footer validation errors on input change so they refresh on next validation
|
|
222
|
-
if (this.props.showLiquidErrorInFooter) {
|
|
223
|
-
this.props.showLiquidErrorInFooter({ STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] });
|
|
224
|
-
}
|
|
225
217
|
}
|
|
226
218
|
|
|
227
219
|
onVersionNameChange() {
|
|
@@ -325,23 +317,7 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
|
|
|
325
317
|
}
|
|
326
318
|
|
|
327
319
|
setFormValidity(isFormValid, errorData) {
|
|
328
|
-
this.setState({
|
|
329
|
-
if (this.state.pendingGetFormData && !isFormValid) {
|
|
330
|
-
this.setState({ pendingGetFormData: false, startValidation: false });
|
|
331
|
-
if (this.props.onValidationFail) {
|
|
332
|
-
this.props.onValidationFail();
|
|
333
|
-
}
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
if (this.state.pendingGetFormData && this.props.getFormSubscriptionData && this.props.isFullMode) {
|
|
337
|
-
if (this.pendingGetFormDataTimeout) {
|
|
338
|
-
clearTimeout(this.pendingGetFormDataTimeout);
|
|
339
|
-
this.pendingGetFormDataTimeout = null;
|
|
340
|
-
}
|
|
341
|
-
this.props.getFormSubscriptionData(this.getFormData());
|
|
342
|
-
this.setState({ pendingGetFormData: false, startValidation: false });
|
|
343
|
-
}
|
|
344
|
-
});
|
|
320
|
+
this.setState({isFormValid, errorData});
|
|
345
321
|
}
|
|
346
322
|
|
|
347
323
|
getFormData(e, value) {
|
|
@@ -947,14 +923,6 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
|
|
|
947
923
|
this.setState({startValidation: false});
|
|
948
924
|
}
|
|
949
925
|
saveFormData() {
|
|
950
|
-
// In library mode: FormBuilder calls onSubmit only after liquid validation succeeds.
|
|
951
|
-
if (!this.props.isFullMode) {
|
|
952
|
-
if (this.state.pendingGetFormData && this.props.getFormSubscriptionData) {
|
|
953
|
-
this.props.getFormSubscriptionData(this.getFormData());
|
|
954
|
-
this.setState({ pendingGetFormData: false, startValidation: false });
|
|
955
|
-
}
|
|
956
|
-
return;
|
|
957
|
-
}
|
|
958
926
|
//Logic to save in db etc
|
|
959
927
|
//saveFormData gets called only when validation result is true
|
|
960
928
|
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import isEmpty from 'lodash/isEmpty';
|
|
2
|
-
import CapNotification from '@capillarytech/cap-ui-library
|
|
2
|
+
import { CapNotification } from '@capillarytech/cap-ui-library';
|
|
3
3
|
import messages from './Create/messages';
|
|
4
4
|
export function showError() {
|
|
5
5
|
const {intl} = this.props;
|
|
6
6
|
const {errorData} = this.state;
|
|
7
7
|
const errorMessage = {key: 'validation-error', message: intl.formatMessage(messages.validationError)};
|
|
8
8
|
if (!isEmpty(this.state.formData) && !this.state.isFormValid) {
|
|
9
|
-
const
|
|
10
|
-
const isSmsInvalid = Object.values(err0).includes(true);
|
|
9
|
+
const isSmsInvalid = Object.values(errorData[0]).includes(true);
|
|
11
10
|
if (isSmsInvalid) {
|
|
11
|
+
const invalidTags = errorData[0]['invalid-tags'];
|
|
12
|
+
if (!isEmpty(invalidTags)) {
|
|
13
|
+
errorMessage.description = `${intl.formatMessage(messages.invalidTags)}: ${invalidTags.join(',')} `;
|
|
14
|
+
}
|
|
12
15
|
CapNotification.error(errorMessage);
|
|
13
16
|
}
|
|
14
17
|
}
|