@capillarytech/creatives-library 8.0.290-alpha.4 → 8.0.291
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 +201 -132
- package/v2Components/FormBuilder/messages.js +8 -0
- package/v2Components/HtmlEditor/HTMLEditor.js +5 -0
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -0
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +15 -0
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +2 -1
- 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 +1 -5
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +5 -13
- package/v2Containers/CreativesContainer/index.js +15 -30
- 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 +104 -4
- package/v2Containers/InApp/tests/index.test.js +6 -17
- package/v2Containers/InappAdvance/index.js +108 -4
- package/v2Containers/InappAdvance/tests/index.test.js +0 -2
- package/v2Containers/Line/Container/Text/index.js +1 -0
- package/v2Containers/MobilePush/Create/index.js +19 -42
- package/v2Containers/MobilePush/Edit/index.js +19 -42
- 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 +2 -17
- package/v2Containers/WebPush/Create/utils/validation.test.js +24 -59
- package/v2Containers/Whatsapp/index.js +17 -9
- package/v2Containers/Zalo/index.js +11 -3
- package/v2Containers/Sms/tests/commonMethods.test.js +0 -122
|
@@ -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
|
}
|
|
@@ -39,8 +39,10 @@ import formBuilderMessages from '../../../v2Components/FormBuilder/messages';
|
|
|
39
39
|
import UnifiedPreview from '../../../v2Components/CommonTestAndPreview/UnifiedPreview';
|
|
40
40
|
import TestAndPreviewSlidebox from '../../../v2Components/TestAndPreviewSlidebox';
|
|
41
41
|
import withCreatives from '../../../hoc/withCreatives';
|
|
42
|
+
import { validateTags } from '../../../utils/tagValidations';
|
|
42
43
|
import {
|
|
43
44
|
CHARLIMIT,
|
|
45
|
+
SMS,
|
|
44
46
|
SMS_TRAI_VAR,
|
|
45
47
|
TAG,
|
|
46
48
|
EMBEDDED,
|
|
@@ -49,15 +51,16 @@ import {
|
|
|
49
51
|
ALL,
|
|
50
52
|
LIBRARY,
|
|
51
53
|
} from './constants';
|
|
52
|
-
import { SMS } from '../../CreativesContainer/constants';
|
|
53
54
|
import v2EditSmsReducer from '../../Sms/Edit/reducer';
|
|
54
55
|
import { v2SmsEditSagas } from '../../Sms/Edit/sagas';
|
|
55
56
|
import ErrorInfoNote from '../../../v2Components/ErrorInfoNote';
|
|
56
57
|
import { validateLiquidTemplateContent } from '../../../utils/commonUtils';
|
|
58
|
+
import { hasLiquidSupportFeature } from '../../../utils/common';
|
|
57
59
|
import { ANDROID } from '../../../v2Components/CommonTestAndPreview/constants';
|
|
58
60
|
|
|
59
61
|
let varMap = {};
|
|
60
62
|
let traiData = {};
|
|
63
|
+
let tagValidationResponse = {};
|
|
61
64
|
const { TextArea } = CapInput;
|
|
62
65
|
const { CapLabelInline } = CapLabel;
|
|
63
66
|
|
|
@@ -91,6 +94,7 @@ export const SmsTraiEdit = (props) => {
|
|
|
91
94
|
const [tags, updateTags] = useState([]);
|
|
92
95
|
const [textAreaId, updateTextAreaId] = useState();
|
|
93
96
|
const [isValidationError, updateIsValidationError] = useState(false);
|
|
97
|
+
const [isTagValidationError, updateIsTagValidationError] = useState(false);
|
|
94
98
|
const [totalMessageLength, setTotalMessageLength] = useState(0);
|
|
95
99
|
const [isUnicodeAllowed, updateIsUnicodeAllowed] = useState(true);
|
|
96
100
|
const [showMsgLengthNote, updateshowMsgLengthNote] = useState(false);
|
|
@@ -225,6 +229,29 @@ export const SmsTraiEdit = (props) => {
|
|
|
225
229
|
}
|
|
226
230
|
}, []);
|
|
227
231
|
|
|
232
|
+
//performs tag validation
|
|
233
|
+
useEffect(() => {
|
|
234
|
+
if (
|
|
235
|
+
!isFullMode &&
|
|
236
|
+
updatedSmsEditor?.length > 0 &&
|
|
237
|
+
!updatedSmsEditor.includes(SMS_TRAI_VAR)
|
|
238
|
+
) {
|
|
239
|
+
tagValidationResponse =
|
|
240
|
+
validateTags({
|
|
241
|
+
content: updatedSmsEditor.join(''),
|
|
242
|
+
tagsParam: tags,
|
|
243
|
+
injectedTagsParams: injectedTags,
|
|
244
|
+
location,
|
|
245
|
+
tagModule: getDefaultTags,
|
|
246
|
+
eventContextTags,
|
|
247
|
+
isFullMode,
|
|
248
|
+
}) || {};
|
|
249
|
+
updateIsTagValidationError(
|
|
250
|
+
tagValidationResponse.unsupportedTags.length > 0,
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
}, [updatedSmsEditor, tags]);
|
|
254
|
+
|
|
228
255
|
const computeUpdatedSmsEditor = () => {
|
|
229
256
|
const arr = [...tempMsgArray];
|
|
230
257
|
const varMapKeys = Object.keys(varMap)?.map((key) => Number(key.slice(8)))?.sort((a, b) => a - b) || [];
|
|
@@ -261,13 +288,6 @@ export const SmsTraiEdit = (props) => {
|
|
|
261
288
|
};
|
|
262
289
|
|
|
263
290
|
const onSubmitWrapper = () => {
|
|
264
|
-
setIsLiquidValidationError(false);
|
|
265
|
-
setLiquidErrorMessages({});
|
|
266
|
-
// Liquid validation (extractTags) only in library mode
|
|
267
|
-
if (isFullMode) {
|
|
268
|
-
onDoneCallback();
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
291
|
const content = updatedSmsEditor.join('');
|
|
272
292
|
const onError = ({ standardErrors, liquidErrors }) => {
|
|
273
293
|
setLiquidErrorMessages({
|
|
@@ -278,8 +298,6 @@ export const SmsTraiEdit = (props) => {
|
|
|
278
298
|
};
|
|
279
299
|
|
|
280
300
|
const onSuccess = () => {
|
|
281
|
-
setIsLiquidValidationError(false);
|
|
282
|
-
setLiquidErrorMessages({});
|
|
283
301
|
onDoneCallback();
|
|
284
302
|
};
|
|
285
303
|
validateLiquidTemplateContent(content, {
|
|
@@ -288,6 +306,10 @@ export const SmsTraiEdit = (props) => {
|
|
|
288
306
|
messages: formBuilderMessages,
|
|
289
307
|
onError,
|
|
290
308
|
onSuccess,
|
|
309
|
+
tagLookupMap: metaEntities?.tagLookupMap,
|
|
310
|
+
eventContextTags,
|
|
311
|
+
isLiquidFlow: true,
|
|
312
|
+
forwardedTags: {},
|
|
291
313
|
});
|
|
292
314
|
};
|
|
293
315
|
|
|
@@ -526,6 +548,17 @@ export const SmsTraiEdit = (props) => {
|
|
|
526
548
|
return countVarChar;
|
|
527
549
|
};
|
|
528
550
|
|
|
551
|
+
const tagValidationErrorMessage = () => {
|
|
552
|
+
const { unsupportedTags = [] } = tagValidationResponse;
|
|
553
|
+
let tagError = '';
|
|
554
|
+
if (unsupportedTags.length > 0) {
|
|
555
|
+
tagError = formatMessage(messages.unsupportedTagsValidationError, {
|
|
556
|
+
unsupportedTags,
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
return <CapError>{tagError}</CapError>;
|
|
560
|
+
};
|
|
561
|
+
|
|
529
562
|
const disablehandler = () => {
|
|
530
563
|
if (traiData && !isEmpty(traiData)) {
|
|
531
564
|
const msg = get(traiData, `versions.base.sms-editor`, '');
|
|
@@ -571,6 +604,7 @@ export const SmsTraiEdit = (props) => {
|
|
|
571
604
|
setShowTestAndPreviewSlidebox(false);
|
|
572
605
|
};
|
|
573
606
|
|
|
607
|
+
const isLiquidSupportFeatureEnabled = hasLiquidSupportFeature();
|
|
574
608
|
return (
|
|
575
609
|
<>
|
|
576
610
|
<CapSpin spinning={loading || fetchingLiquidTags} tip={fetchingLiquidTags && formatMessage(formBuilderMessages.liquidSpinText)}>
|
|
@@ -628,6 +662,7 @@ export const SmsTraiEdit = (props) => {
|
|
|
628
662
|
<CapRow>
|
|
629
663
|
{smsLengthForVar()}
|
|
630
664
|
</CapRow>
|
|
665
|
+
{isTagValidationError && tagValidationErrorMessage()}
|
|
631
666
|
<CapCheckbox onChange={unicodeHandler} checked={isUnicodeAllowed} disabled={disablehandler()}>
|
|
632
667
|
{formatMessage(messages.unicodeLabel)}
|
|
633
668
|
</CapCheckbox>
|
|
@@ -658,8 +693,9 @@ export const SmsTraiEdit = (props) => {
|
|
|
658
693
|
<FormattedMessage {...messages.testAndPreviewButtonLabel} />
|
|
659
694
|
</CapButton>
|
|
660
695
|
<CapButton
|
|
661
|
-
onClick={onSubmitWrapper}
|
|
696
|
+
onClick={isLiquidSupportFeatureEnabled ? onSubmitWrapper : onDoneCallback}
|
|
662
697
|
className="create-msg"
|
|
698
|
+
disabled={isTagValidationError || (isLiquidSupportFeatureEnabled && !isObject(metaEntities?.tagLookupMap))}
|
|
663
699
|
>
|
|
664
700
|
<FormattedMessage {...messages.saveButtonLabel} />
|
|
665
701
|
</CapButton>
|
|
@@ -4265,7 +4265,7 @@ FREE GIFTS-
|
|
|
4265
4265
|
<CapCheckbox
|
|
4266
4266
|
checked={false}
|
|
4267
4267
|
disabled={false}
|
|
4268
|
-
key=".
|
|
4268
|
+
key=".4"
|
|
4269
4269
|
labelType="h4"
|
|
4270
4270
|
onChange={[Function]}
|
|
4271
4271
|
>
|
|
@@ -4327,7 +4327,7 @@ FREE GIFTS-
|
|
|
4327
4327
|
</div>
|
|
4328
4328
|
</CapCheckbox>
|
|
4329
4329
|
<div
|
|
4330
|
-
key=".
|
|
4330
|
+
key=".6"
|
|
4331
4331
|
style={
|
|
4332
4332
|
Object {
|
|
4333
4333
|
"marginBottom": "100px",
|
|
@@ -15176,7 +15176,7 @@ FREE GIFTS-
|
|
|
15176
15176
|
<CapCheckbox
|
|
15177
15177
|
checked={false}
|
|
15178
15178
|
disabled={false}
|
|
15179
|
-
key=".
|
|
15179
|
+
key=".4"
|
|
15180
15180
|
labelType="h4"
|
|
15181
15181
|
onChange={[Function]}
|
|
15182
15182
|
>
|
|
@@ -15238,7 +15238,7 @@ FREE GIFTS-
|
|
|
15238
15238
|
</div>
|
|
15239
15239
|
</CapCheckbox>
|
|
15240
15240
|
<div
|
|
15241
|
-
key=".
|
|
15241
|
+
key=".6"
|
|
15242
15242
|
style={
|
|
15243
15243
|
Object {
|
|
15244
15244
|
"marginBottom": "100px",
|
|
@@ -26117,7 +26117,7 @@ FREE GIFTS-
|
|
|
26117
26117
|
<CapCheckbox
|
|
26118
26118
|
checked={false}
|
|
26119
26119
|
disabled={false}
|
|
26120
|
-
key=".
|
|
26120
|
+
key=".4"
|
|
26121
26121
|
labelType="h4"
|
|
26122
26122
|
onChange={[Function]}
|
|
26123
26123
|
>
|
|
@@ -26179,7 +26179,7 @@ FREE GIFTS-
|
|
|
26179
26179
|
</div>
|
|
26180
26180
|
</CapCheckbox>
|
|
26181
26181
|
<div
|
|
26182
|
-
key=".
|
|
26182
|
+
key=".6"
|
|
26183
26183
|
style={
|
|
26184
26184
|
Object {
|
|
26185
26185
|
"marginBottom": "100px",
|
|
@@ -36,7 +36,6 @@ const SmsWrapper = (props) => {
|
|
|
36
36
|
handleTestAndPreview,
|
|
37
37
|
handleCloseTestAndPreview,
|
|
38
38
|
isTestAndPreviewMode,
|
|
39
|
-
onValidationFail,
|
|
40
39
|
} = props;
|
|
41
40
|
|
|
42
41
|
const smsProps = {
|
|
@@ -59,7 +58,6 @@ const SmsWrapper = (props) => {
|
|
|
59
58
|
handleTestAndPreview,
|
|
60
59
|
handleCloseTestAndPreview,
|
|
61
60
|
isTestAndPreviewMode,
|
|
62
|
-
onValidationFail,
|
|
63
61
|
};
|
|
64
62
|
const isTraiDlt = isTraiDLTEnable(isFullMode, smsRegister);
|
|
65
63
|
return <>
|
|
@@ -105,10 +105,12 @@ export const useTagManagement = ({
|
|
|
105
105
|
const validationConfig = useMemo(
|
|
106
106
|
() => ({
|
|
107
107
|
tagsParam: tags,
|
|
108
|
+
injectedTagsParams: injectedTags,
|
|
108
109
|
location,
|
|
109
110
|
tagModule: getDefaultTags,
|
|
111
|
+
eventContextTags,
|
|
110
112
|
}),
|
|
111
|
-
[tags, location, getDefaultTags],
|
|
113
|
+
[tags, injectedTags, location, getDefaultTags, eventContextTags],
|
|
112
114
|
);
|
|
113
115
|
|
|
114
116
|
return {
|
|
@@ -528,19 +528,26 @@ describe('useTagManagement', () => {
|
|
|
528
528
|
|
|
529
529
|
describe('validationConfig', () => {
|
|
530
530
|
it('should return validation config with tags', () => {
|
|
531
|
+
const injectedTags = [{ id: 5, name: 'Injected Tag' }];
|
|
532
|
+
const eventContextTags = [{ id: 6, name: 'Event Tag' }];
|
|
533
|
+
|
|
531
534
|
const { result } = renderHook(() =>
|
|
532
535
|
useTagManagement({
|
|
533
536
|
location: defaultLocation,
|
|
534
537
|
globalActions: mockGlobalActions,
|
|
535
538
|
metaEntities: defaultMetaEntities,
|
|
539
|
+
injectedTags,
|
|
540
|
+
eventContextTags,
|
|
536
541
|
getDefaultTags: 'custom',
|
|
537
542
|
})
|
|
538
543
|
);
|
|
539
544
|
|
|
540
545
|
expect(result.current.validationConfig).toEqual({
|
|
541
546
|
tagsParam: defaultMetaEntities.tags.standard,
|
|
547
|
+
injectedTagsParams: injectedTags,
|
|
542
548
|
location: defaultLocation,
|
|
543
549
|
tagModule: 'custom',
|
|
550
|
+
eventContextTags,
|
|
544
551
|
});
|
|
545
552
|
});
|
|
546
553
|
|
|
@@ -295,8 +295,8 @@ const WebPushCreate = ({
|
|
|
295
295
|
const validateTemplateName = useCallback((value) => validateTemplateNameUtil(value), []);
|
|
296
296
|
|
|
297
297
|
const validateTitle = useCallback(
|
|
298
|
-
(value) => validateTitleUtil(value, formatMessage, messages, restrictPersonalization
|
|
299
|
-
[formatMessage, restrictPersonalization
|
|
298
|
+
(value) => validateTitleUtil(value, formatMessage, messages, restrictPersonalization),
|
|
299
|
+
[formatMessage, restrictPersonalization],
|
|
300
300
|
);
|
|
301
301
|
|
|
302
302
|
const validateUrl = useCallback(
|