@capillarytech/creatives-library 8.0.286 → 8.0.287
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/utils/commonUtils.js +3 -0
- package/v2Components/CapTagList/index.js +6 -2
- package/v2Components/CapTagListWithInput/index.js +4 -0
- package/v2Components/FormBuilder/index.js +26 -3
- package/v2Components/FormBuilder/messages.js +4 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +20 -0
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +39 -3
- package/v2Containers/CreativesContainer/constants.js +6 -0
- package/v2Containers/CreativesContainer/index.js +32 -1
- package/v2Containers/CreativesContainer/messages.js +12 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +339 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +18 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +37 -0
- package/v2Containers/MobilePush/Create/index.js +45 -0
- package/v2Containers/MobilePush/Create/messages.js +4 -0
- package/v2Containers/MobilePush/Edit/index.js +45 -0
- package/v2Containers/MobilePush/Edit/messages.js +4 -0
- package/v2Containers/MobilePushNew/components/PlatformContentFields.js +36 -12
- package/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +68 -27
- package/v2Containers/MobilePushNew/index.js +32 -3
- package/v2Containers/MobilePushNew/messages.js +8 -0
- package/v2Containers/MobilepushWrapper/index.js +7 -1
- package/v2Containers/SmsTrai/Create/index.scss +1 -1
- package/v2Containers/TagList/index.js +17 -1
- package/v2Containers/TagList/messages.js +4 -0
- package/v2Containers/TemplatesV2/index.js +43 -23
- package/v2Containers/Viber/index.scss +1 -1
- package/v2Containers/WebPush/Create/index.js +25 -6
- package/v2Containers/WebPush/Create/messages.js +8 -1
- package/v2Containers/WebPush/Create/utils/validation.js +16 -9
- package/v2Containers/WebPush/Create/utils/validation.test.js +28 -0
package/package.json
CHANGED
package/utils/commonUtils.js
CHANGED
|
@@ -476,7 +476,9 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
|
|
|
476
476
|
>
|
|
477
477
|
<CapTooltip
|
|
478
478
|
title={
|
|
479
|
-
|
|
479
|
+
this?.props?.disableTooltipMsg && this?.props?.disabled ? (
|
|
480
|
+
this?.props?.disableTooltipMsg
|
|
481
|
+
) : fetchingSchemaError ? (
|
|
480
482
|
<CapRow className="tooltip-text-container">
|
|
481
483
|
<CapLabel className="tooltip-text1">
|
|
482
484
|
{formatMessage(messages.somethingWentWrong)}
|
|
@@ -485,7 +487,7 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
|
|
|
485
487
|
{formatMessage(messages.labelFetchErrorMsg)}
|
|
486
488
|
</CapLabel>
|
|
487
489
|
</CapRow>
|
|
488
|
-
)
|
|
490
|
+
) : null
|
|
489
491
|
}
|
|
490
492
|
placement="right"
|
|
491
493
|
>
|
|
@@ -538,6 +540,8 @@ CapTagList.propTypes = {
|
|
|
538
540
|
currentOrgDetails: PropTypes.object,
|
|
539
541
|
channel: PropTypes.string,
|
|
540
542
|
disabled: PropTypes.bool,
|
|
543
|
+
// Optional custom tooltip message when disabled (used for personalization restriction)
|
|
544
|
+
disableTooltipMsg: PropTypes.string,
|
|
541
545
|
fetchingSchemaError: PropTypes.bool,
|
|
542
546
|
popoverPlacement: PropTypes.string,
|
|
543
547
|
};
|
|
@@ -26,6 +26,7 @@ export const CapTagListWithInput = (props) => {
|
|
|
26
26
|
className = '',
|
|
27
27
|
userLocale = 'en',
|
|
28
28
|
eventContextTags = [],
|
|
29
|
+
restrictPersonalization = false,
|
|
29
30
|
// CapInput props
|
|
30
31
|
inputId,
|
|
31
32
|
inputValue = '',
|
|
@@ -78,6 +79,7 @@ export const CapTagListWithInput = (props) => {
|
|
|
78
79
|
eventContextTags={eventContextTags}
|
|
79
80
|
style={tagListStyle}
|
|
80
81
|
popoverPlacement={popoverPlacement}
|
|
82
|
+
restrictPersonalization={restrictPersonalization}
|
|
81
83
|
/>
|
|
82
84
|
)}
|
|
83
85
|
</CapRow>
|
|
@@ -113,6 +115,7 @@ CapTagListWithInput.propTypes = {
|
|
|
113
115
|
className: PropTypes.string,
|
|
114
116
|
userLocale: PropTypes.string,
|
|
115
117
|
eventContextTags: PropTypes.array,
|
|
118
|
+
restrictPersonalization: PropTypes.bool,
|
|
116
119
|
|
|
117
120
|
// CapInput props
|
|
118
121
|
inputId: PropTypes.string.isRequired,
|
|
@@ -150,6 +153,7 @@ CapTagListWithInput.defaultProps = {
|
|
|
150
153
|
className: '',
|
|
151
154
|
userLocale: 'en',
|
|
152
155
|
eventContextTags: [],
|
|
156
|
+
restrictPersonalization: false,
|
|
153
157
|
inputValue: '',
|
|
154
158
|
inputSize: 'default',
|
|
155
159
|
inputRequired: false,
|
|
@@ -63,7 +63,7 @@ import { REQUEST } from '../../v2Containers/Cap/constants'
|
|
|
63
63
|
import { hasLiquidSupportFeature, isEmailUnsubscribeTagMandatory } from '../../utils/common';
|
|
64
64
|
import { isUrl } from '../../v2Containers/Line/Container/Wrapper/utils';
|
|
65
65
|
import { bindActionCreators } from 'redux';
|
|
66
|
-
import { getChannelData, validateLiquidTemplateContent, validateMobilePushContent } from '../../utils/commonUtils';
|
|
66
|
+
import { getChannelData, hasPersonalizationTags, validateLiquidTemplateContent, validateMobilePushContent } from '../../utils/commonUtils';
|
|
67
67
|
const TabPane = Tabs.TabPane;
|
|
68
68
|
const {Column} = Table;
|
|
69
69
|
const {TextArea} = CapInput;
|
|
@@ -903,11 +903,14 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
903
903
|
if (errorData[parseInt(index)]) {
|
|
904
904
|
if (message) {
|
|
905
905
|
message = message.trim();
|
|
906
|
-
|
|
907
906
|
if (message === "") {
|
|
908
907
|
errorData[parseInt(index)][`message-editor${selector}`] = true;
|
|
909
908
|
isValid = false;
|
|
910
909
|
isCurrentTabValid = false;
|
|
910
|
+
} else if (this.props.restrictPersonalization && hasPersonalizationTags(message)) {
|
|
911
|
+
errorData[parseInt(index)][`message-editor${selector}`] = true;
|
|
912
|
+
isValid = false;
|
|
913
|
+
isCurrentTabValid = false;
|
|
911
914
|
} else {
|
|
912
915
|
errorData[parseInt(index)][`message-editor${selector}`] = false;
|
|
913
916
|
const tagValidationResponse = this.validateTags(message, tags, injectedTags, false, this.props?.isFullMode);
|
|
@@ -937,6 +940,10 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
937
940
|
errorData[parseInt(index)][`message-title${selector}`] = true;
|
|
938
941
|
isValid = false;
|
|
939
942
|
isCurrentTabValid = false;
|
|
943
|
+
} else if (this.props.restrictPersonalization && hasPersonalizationTags(title)) {
|
|
944
|
+
errorData[parseInt(index)][`message-title${selector}`] = true;
|
|
945
|
+
isValid = false;
|
|
946
|
+
isCurrentTabValid = false;
|
|
940
947
|
} else {
|
|
941
948
|
errorData[parseInt(index)][`message-title${selector}`] = false;
|
|
942
949
|
const tagValidationResponse = this.validateTags(title, tags, injectedTags, false, this.props?.isFullMode);
|
|
@@ -2882,6 +2889,10 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
2882
2889
|
default:
|
|
2883
2890
|
break;
|
|
2884
2891
|
}
|
|
2892
|
+
|
|
2893
|
+
if (this.props.restrictPersonalization && hasPersonalizationTags(messageContent)) {
|
|
2894
|
+
errorMessageText = formatMessage(messages.personalizationTagsErrorMessage);
|
|
2895
|
+
}
|
|
2885
2896
|
const prevErrorMessage = this.state.liquidErrorMessage?.STANDARD_ERROR_MSG?.[0];
|
|
2886
2897
|
if (prevErrorMessage !== errorMessageText && errorMessageText && this.liquidFlow()) {
|
|
2887
2898
|
this.setState((prevState) => ({
|
|
@@ -3047,6 +3058,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3047
3058
|
userLocale={this.props.userLocale}
|
|
3048
3059
|
selectedOfferDetails={this.props.selectedOfferDetails}
|
|
3049
3060
|
eventContextTags={this.props?.eventContextTags}
|
|
3061
|
+
restrictPersonalization={this.props.restrictPersonalization}
|
|
3050
3062
|
/>
|
|
3051
3063
|
</CapColumn>
|
|
3052
3064
|
);
|
|
@@ -3081,6 +3093,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3081
3093
|
inputProps={val.inputProps || {}}
|
|
3082
3094
|
showInput={val.showInput !== false}
|
|
3083
3095
|
showTagList={val.showTagList !== false}
|
|
3096
|
+
restrictPersonalization={this.props.restrictPersonalization}
|
|
3084
3097
|
/>
|
|
3085
3098
|
</CapColumn>
|
|
3086
3099
|
);
|
|
@@ -3457,8 +3470,15 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3457
3470
|
ifError = this.state.checkValidation && (isVersionEnable ? this.state.errorData[`${this.state.currentTab - 1}`][val.id] : this.state.errorData[val.id]);
|
|
3458
3471
|
const { TAG_BRACKET_COUNT_MISMATCH_ERROR } = errorMessageForTags;
|
|
3459
3472
|
const { formatMessage } = this.props.intl;
|
|
3460
|
-
let errorMessageText = errorType === TAG_BRACKET_COUNT_MISMATCH_ERROR ? formatMessage(globalMessages.unbalanacedCurlyBraces) :(val.errorMessage && ifError ? val.errorMessage : '');
|
|
3461
3473
|
const value = isVersionEnable ? this.state.formData[`${this.state.currentTab - 1}`][val.id] : this.state.formData[val.id];
|
|
3474
|
+
// Show personalization error for title field inline (same as message textarea) when restrictPersonalization is true
|
|
3475
|
+
const hasPersonalizationError = this.props.restrictPersonalization && hasPersonalizationTags(value);
|
|
3476
|
+
if (hasPersonalizationError) {
|
|
3477
|
+
ifError = true;
|
|
3478
|
+
}
|
|
3479
|
+
let errorMessageText = hasPersonalizationError
|
|
3480
|
+
? formatMessage(messages.personalizationTagsErrorMessage)
|
|
3481
|
+
: (errorType === TAG_BRACKET_COUNT_MISMATCH_ERROR ? formatMessage(globalMessages.unbalanacedCurlyBraces) : (val.errorMessage && ifError ? val.errorMessage : ''));
|
|
3462
3482
|
if (styling === 'semantic') {
|
|
3463
3483
|
columns.push(
|
|
3464
3484
|
<CapColumn key={val.id} span={val.width} offset={val.offset} style={val.style || {}}>
|
|
@@ -3709,6 +3729,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3709
3729
|
selectedOfferDetails={this.props.selectedOfferDetails}
|
|
3710
3730
|
channel={channel}
|
|
3711
3731
|
eventContextTags={this.props?.eventContextTags}
|
|
3732
|
+
restrictPersonalization={this.props.restrictPersonalization}
|
|
3712
3733
|
/>
|
|
3713
3734
|
</CapColumn>
|
|
3714
3735
|
);
|
|
@@ -3760,6 +3781,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3760
3781
|
inputProps={val.inputProps || {}}
|
|
3761
3782
|
showInput={val.showInput !== false}
|
|
3762
3783
|
showTagList={val.showTagList !== false}
|
|
3784
|
+
restrictPersonalization={this.props.restrictPersonalization}
|
|
3763
3785
|
/>
|
|
3764
3786
|
</CapColumn>
|
|
3765
3787
|
);
|
|
@@ -4403,6 +4425,7 @@ FormBuilder.propTypes = {
|
|
|
4403
4425
|
forwardedTags: PropTypes.object.isRequired,
|
|
4404
4426
|
isLoyaltyModule: PropTypes.bool.isRequired,
|
|
4405
4427
|
isTestAndPreviewMode: PropTypes.bool, // Add new prop type
|
|
4428
|
+
restrictPersonalization: PropTypes.bool,
|
|
4406
4429
|
};
|
|
4407
4430
|
|
|
4408
4431
|
const mapStateToProps = createStructuredSelector({
|
|
@@ -102,4 +102,8 @@ export default defineMessages({
|
|
|
102
102
|
id: 'creatives.componentsV2.FormBuilder.base64ImageError',
|
|
103
103
|
defaultMessage: 'Base64 images are not allowed. Please upload your image to a gallery and use it, or add the image URL instead.',
|
|
104
104
|
},
|
|
105
|
+
personalizationTagsErrorMessage: {
|
|
106
|
+
id: `creatives.componentsV2.FormBuilder.personalizationTagsErrorMessage`,
|
|
107
|
+
defaultMessage: 'Personalization tags are not supported for anonymous customers, please remove the tags.',
|
|
108
|
+
},
|
|
105
109
|
});
|
|
@@ -173,6 +173,9 @@ export function SlideBoxContent(props) {
|
|
|
173
173
|
showTestAndPreviewSlidebox,
|
|
174
174
|
handleTestAndPreview,
|
|
175
175
|
handleCloseTestAndPreview,
|
|
176
|
+
restrictPersonalization = false,
|
|
177
|
+
isAnonymousType = false,
|
|
178
|
+
onPersonalizationTokensChange,
|
|
176
179
|
isTestAndPreviewMode,
|
|
177
180
|
onHtmlEditorValidationStateChange,
|
|
178
181
|
} = props;
|
|
@@ -637,6 +640,8 @@ export function SlideBoxContent(props) {
|
|
|
637
640
|
smsRegister={smsRegister}
|
|
638
641
|
onShowTemplates={onShowTemplates}
|
|
639
642
|
eventContextTags={eventContextTags}
|
|
643
|
+
restrictPersonalization={restrictPersonalization}
|
|
644
|
+
isAnonymousType={isAnonymousType}
|
|
640
645
|
/>
|
|
641
646
|
)}
|
|
642
647
|
|
|
@@ -677,6 +682,8 @@ export function SlideBoxContent(props) {
|
|
|
677
682
|
isTestAndPreviewMode={isTestAndPreviewMode}
|
|
678
683
|
location={location}
|
|
679
684
|
onHtmlEditorValidationStateChange={onHtmlEditorValidationStateChange}
|
|
685
|
+
restrictPersonalization={restrictPersonalization}
|
|
686
|
+
isAnonymousType={isAnonymousType}
|
|
680
687
|
/>
|
|
681
688
|
)}
|
|
682
689
|
{(isEditEmailWithId || isEmailEditWithContent) && (
|
|
@@ -797,6 +804,9 @@ export function SlideBoxContent(props) {
|
|
|
797
804
|
showTestAndPreviewSlidebox={showTestAndPreviewSlidebox}
|
|
798
805
|
handleTestAndPreview={handleTestAndPreview}
|
|
799
806
|
handleCloseTestAndPreview={handleCloseTestAndPreview}
|
|
807
|
+
restrictPersonalization={restrictPersonalization}
|
|
808
|
+
isAnonymousType={isAnonymousType}
|
|
809
|
+
onPersonalizationTokensChange={onPersonalizationTokensChange}
|
|
800
810
|
/>
|
|
801
811
|
) : (
|
|
802
812
|
<MobilePushNew
|
|
@@ -824,6 +834,9 @@ export function SlideBoxContent(props) {
|
|
|
824
834
|
eventContextTags={eventContextTags}
|
|
825
835
|
showLiquidErrorInFooter={showLiquidErrorInFooter}
|
|
826
836
|
handleClose={handleClose}
|
|
837
|
+
restrictPersonalization={restrictPersonalization}
|
|
838
|
+
isAnonymousType={isAnonymousType}
|
|
839
|
+
onPersonalizationTokensChange={onPersonalizationTokensChange}
|
|
827
840
|
/>
|
|
828
841
|
)
|
|
829
842
|
)}
|
|
@@ -861,6 +874,9 @@ export function SlideBoxContent(props) {
|
|
|
861
874
|
showTestAndPreviewSlidebox={showTestAndPreviewSlidebox}
|
|
862
875
|
handleTestAndPreview={handleTestAndPreview}
|
|
863
876
|
handleCloseTestAndPreview={handleCloseTestAndPreview}
|
|
877
|
+
restrictPersonalization={restrictPersonalization}
|
|
878
|
+
isAnonymousType={isAnonymousType}
|
|
879
|
+
onPersonalizationTokensChange={onPersonalizationTokensChange}
|
|
864
880
|
/>
|
|
865
881
|
) : (
|
|
866
882
|
<MobilePushNew
|
|
@@ -895,6 +911,8 @@ export function SlideBoxContent(props) {
|
|
|
895
911
|
showLiquidErrorInFooter={showLiquidErrorInFooter}
|
|
896
912
|
onCreateComplete={onCreateComplete}
|
|
897
913
|
creativesMode={creativesMode}
|
|
914
|
+
restrictPersonalization={restrictPersonalization}
|
|
915
|
+
isAnonymousType={isAnonymousType}
|
|
898
916
|
/>
|
|
899
917
|
)
|
|
900
918
|
)}
|
|
@@ -1143,6 +1161,8 @@ export function SlideBoxContent(props) {
|
|
|
1143
1161
|
supportedTags={supportedTags}
|
|
1144
1162
|
selectedOfferDetails={selectedOfferDetails}
|
|
1145
1163
|
eventContextTags={eventContextTags}
|
|
1164
|
+
restrictPersonalization={restrictPersonalization}
|
|
1165
|
+
isAnonymousType={isAnonymousType}
|
|
1146
1166
|
/>
|
|
1147
1167
|
)}
|
|
1148
1168
|
{isCreateRcs && (<Rcs
|
|
@@ -43,8 +43,11 @@ function SlideBoxFooter(props) {
|
|
|
43
43
|
currentChannel,
|
|
44
44
|
emailCreateMode,
|
|
45
45
|
selectedEmailCreateMode,
|
|
46
|
+
restrictPersonalization = false,
|
|
47
|
+
isAnonymousType = false,
|
|
48
|
+
templateData = {},
|
|
49
|
+
hasPersonalizationTokenError: hasPersonalizationTokenErrorProp = false,
|
|
46
50
|
} = props;
|
|
47
|
-
|
|
48
51
|
// Calculate if buttons should be disabled
|
|
49
52
|
// Only apply validation state checks for EMAIL channel in HTML Editor mode (not BEE/DragDrop)
|
|
50
53
|
// For other channels, BEE editor, or when htmlEditorValidationState is not provided, don't disable based on validation
|
|
@@ -127,6 +130,32 @@ function SlideBoxFooter(props) {
|
|
|
127
130
|
const hasBEEEditorErrors = isEmailChannel && isBEEEditorMode && (hasStandardErrors || hasLiquidErrors) && (!htmlEditorValidationState || !htmlEditorHasErrors);
|
|
128
131
|
|
|
129
132
|
const shouldShowErrorInfoNote = hasBEEEditorErrors || isSupportCKEditor;
|
|
133
|
+
|
|
134
|
+
// Check for personalization tokens in title/message when anonymous user tries to save
|
|
135
|
+
const hasPersonalizationTokens = () => {
|
|
136
|
+
if (!isAnonymousType || !templateData) return false;
|
|
137
|
+
|
|
138
|
+
const androidTitle = templateData?.versions?.ANDROID?.base?.title || '';
|
|
139
|
+
const iosTitle = templateData?.versions?.IOS?.base?.title || '';
|
|
140
|
+
const androidMessageBody = templateData?.versions?.ANDROID?.base?.expandibleDetails?.message
|
|
141
|
+
|| templateData?.versions?.ANDROID?.base?.expandableDetails?.message || '';
|
|
142
|
+
const iosMessageBody = templateData?.versions?.IOS?.base?.expandibleDetails?.message
|
|
143
|
+
|| templateData?.versions?.IOS?.base?.expandableDetails?.message || '';
|
|
144
|
+
const contentToCheck = `${androidTitle} ${iosTitle} ${androidMessageBody} ${iosMessageBody}`;
|
|
145
|
+
// Check for liquid tags {{ }}
|
|
146
|
+
if (contentToCheck.includes('{{') && contentToCheck.includes('}}')) {
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
// Check for event context tags [
|
|
150
|
+
if (contentToCheck.includes('[') && contentToCheck.includes(']')) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
return false;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Use prop from parent (FormBuilder flow) when available; else fall back to templateData check
|
|
157
|
+
const hasPersonalizationTokenError = hasPersonalizationTokenErrorProp === true || (restrictPersonalization && hasPersonalizationTokens());
|
|
158
|
+
|
|
130
159
|
return (
|
|
131
160
|
<div className="template-footer-width">
|
|
132
161
|
{shouldShowErrorInfoNote && (
|
|
@@ -147,7 +176,7 @@ function SlideBoxFooter(props) {
|
|
|
147
176
|
<CapRow>
|
|
148
177
|
<CapButton
|
|
149
178
|
onClick={onSave}
|
|
150
|
-
disabled={isTemplateNameEmpty || fetchingCmsData || shouldDisableButtons}
|
|
179
|
+
disabled={isTemplateNameEmpty || fetchingCmsData || shouldDisableButtons || hasPersonalizationTokenError}
|
|
151
180
|
>
|
|
152
181
|
{isFullMode ? (
|
|
153
182
|
getFullModeSaveBtn(slidBoxContent, isCreatingTemplate)
|
|
@@ -159,7 +188,7 @@ function SlideBoxFooter(props) {
|
|
|
159
188
|
<CapButton
|
|
160
189
|
type="secondary"
|
|
161
190
|
onClick={onTestAndPreview}
|
|
162
|
-
disabled={shouldDisableButtons}
|
|
191
|
+
disabled={shouldDisableButtons || hasPersonalizationTokenError}
|
|
163
192
|
style={{ marginLeft: '8px' }}
|
|
164
193
|
>
|
|
165
194
|
<FormattedMessage {...messages.testAndPreview} />
|
|
@@ -220,6 +249,11 @@ SlideBoxFooter.propTypes = {
|
|
|
220
249
|
currentChannel: PropTypes.string,
|
|
221
250
|
emailCreateMode: PropTypes.string,
|
|
222
251
|
selectedEmailCreateMode: PropTypes.string,
|
|
252
|
+
restrictPersonalization: PropTypes.bool,
|
|
253
|
+
isAnonymousType: PropTypes.bool,
|
|
254
|
+
templateData: PropTypes.object,
|
|
255
|
+
formData: PropTypes.array,
|
|
256
|
+
hasPersonalizationTokenError: PropTypes.bool,
|
|
223
257
|
};
|
|
224
258
|
|
|
225
259
|
SlideBoxFooter.defaultProps = {
|
|
@@ -245,5 +279,7 @@ SlideBoxFooter.defaultProps = {
|
|
|
245
279
|
currentChannel: '',
|
|
246
280
|
emailCreateMode: '',
|
|
247
281
|
selectedEmailCreateMode: '',
|
|
282
|
+
formData: [],
|
|
283
|
+
hasPersonalizationTokenError: false,
|
|
248
284
|
};
|
|
249
285
|
export default SlideBoxFooter;
|
|
@@ -52,3 +52,9 @@ export const GENERIC = "GENERIC";
|
|
|
52
52
|
export const LIQUID_ERROR_MSG = "LIQUID_ERROR_MSG";
|
|
53
53
|
export const STANDARD_ERROR_MSG = "STANDARD_ERROR_MSG";
|
|
54
54
|
export const COMMON_CHANNELS = ['sms', 'email', 'wechat', 'mobilepush', 'webpush', 'line', 'viber', 'facebook', 'call_task', 'ftp', 'assets'];
|
|
55
|
+
export const MIXED = "MIXED";
|
|
56
|
+
export const VISITOR = "VISITOR";
|
|
57
|
+
export const ALLOWED_CHANNELS_FOR_ANONYMOUS = ['mobilepush', 'webpush'];
|
|
58
|
+
export const ALL_CHANNELS_NEW = [
|
|
59
|
+
'sms', 'email', 'whatsapp', 'facebook', 'line', 'viber', 'rcs', 'zalo', 'inapp', 'call_task', 'ftp',
|
|
60
|
+
];
|
|
@@ -130,6 +130,7 @@ export class Creatives extends React.Component {
|
|
|
130
130
|
validationComplete: false, // Flag to track if validation has completed
|
|
131
131
|
errorsAcknowledged: false, // Flag to track if user has acknowledged errors by clicking redirection icon
|
|
132
132
|
},
|
|
133
|
+
hasPersonalizationTokenError: false, // Track personalization token errors in form
|
|
133
134
|
};
|
|
134
135
|
this.liquidFlow = Boolean(commonUtil.hasLiquidSupportFeature());
|
|
135
136
|
this.creativesTemplateSteps = {
|
|
@@ -1777,6 +1778,12 @@ export class Creatives extends React.Component {
|
|
|
1777
1778
|
});
|
|
1778
1779
|
}
|
|
1779
1780
|
|
|
1781
|
+
handlePersonalizationTokensChange = (hasTokens) => {
|
|
1782
|
+
this.setState({
|
|
1783
|
+
hasPersonalizationTokenError: hasTokens,
|
|
1784
|
+
});
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1780
1787
|
shouldShowContinueFooter = () => { // only for email for now, has to be modified according to channel
|
|
1781
1788
|
const {
|
|
1782
1789
|
slidBoxContent, templateStep, currentChannel, emailCreateMode, mobilePushCreateMode, inAppEditorType, weChatTemplateType,
|
|
@@ -1944,6 +1951,21 @@ export class Creatives extends React.Component {
|
|
|
1944
1951
|
? CAP_SPACE_32
|
|
1945
1952
|
: 0;
|
|
1946
1953
|
/* TODO: Instead of passing down same props separately to each component down, write common function to these props and pass it accordingly */
|
|
1954
|
+
|
|
1955
|
+
// Compute anonymous user type and channel restrictions
|
|
1956
|
+
const { customerType = '' } = this.props;
|
|
1957
|
+
const isAnonymousType = customerType === constants.VISITOR || customerType === constants.MIXED;
|
|
1958
|
+
const restrictPersonalization = isAnonymousType;
|
|
1959
|
+
|
|
1960
|
+
// For anonymous users, only MOBILEPUSH and WEBPUSH channels are allowed
|
|
1961
|
+
let updatedChannelsToHide = [...(channelsToHide || [])];
|
|
1962
|
+
if (isAnonymousType) {
|
|
1963
|
+
const allowedChannels = constants.ALLOWED_CHANNELS_FOR_ANONYMOUS;
|
|
1964
|
+
const allChannels = constants.ALL_CHANNELS_NEW;
|
|
1965
|
+
const channelsToRemove = allChannels.filter((channel) => !allowedChannels.includes(channel));
|
|
1966
|
+
updatedChannelsToHide = [...new Set([...updatedChannelsToHide, ...channelsToRemove])];
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1947
1969
|
return (
|
|
1948
1970
|
<SlideBoxWrapper
|
|
1949
1971
|
slideBoxWrapperMargin={slideBoxWrapperMargin}
|
|
@@ -2014,10 +2036,13 @@ export class Creatives extends React.Component {
|
|
|
2014
2036
|
mobilePushCreateMode={mobilePushCreateMode}
|
|
2015
2037
|
showTemplateName={this.showTemplateName}
|
|
2016
2038
|
onValidationFail={this.onValidationFail}
|
|
2017
|
-
channelsToHide={
|
|
2039
|
+
channelsToHide={updatedChannelsToHide}
|
|
2018
2040
|
forwardedTags={forwardedTags}
|
|
2019
2041
|
selectedOfferDetails={selectedOfferDetails}
|
|
2020
2042
|
channelsToDisable={channelsToDisable}
|
|
2043
|
+
restrictPersonalization={restrictPersonalization}
|
|
2044
|
+
isAnonymousType={isAnonymousType}
|
|
2045
|
+
customerType={customerType}
|
|
2021
2046
|
weChatTemplateType={weChatTemplateType}
|
|
2022
2047
|
onWechatTemplateChange={this.onWechatTemplateChange}
|
|
2023
2048
|
weChatMaptemplateStep={weChatMaptemplateStep}
|
|
@@ -2050,6 +2075,7 @@ export class Creatives extends React.Component {
|
|
|
2050
2075
|
handleCloseTestAndPreview={this.handleCloseTestAndPreview}
|
|
2051
2076
|
isTestAndPreviewMode={(() => this.state.isTestAndPreviewMode)()}
|
|
2052
2077
|
onHtmlEditorValidationStateChange={this.updateHtmlEditorValidationState}
|
|
2078
|
+
onPersonalizationTokensChange={this.handlePersonalizationTokensChange}
|
|
2053
2079
|
/>
|
|
2054
2080
|
)}
|
|
2055
2081
|
footer={this.shouldShowFooter() ? (
|
|
@@ -2082,6 +2108,10 @@ export class Creatives extends React.Component {
|
|
|
2082
2108
|
})()}
|
|
2083
2109
|
htmlEditorValidationState={htmlEditorValidationState}
|
|
2084
2110
|
isCreatingTemplate={slidBoxContent === 'createTemplate' && currentChannel.toUpperCase() === constants.EMAIL}
|
|
2111
|
+
restrictPersonalization={restrictPersonalization}
|
|
2112
|
+
isAnonymousType={isAnonymousType}
|
|
2113
|
+
templateData={templateData}
|
|
2114
|
+
hasPersonalizationTokenError={this.state.hasPersonalizationTokenError}
|
|
2085
2115
|
/>
|
|
2086
2116
|
) : isLiquidValidationError && (
|
|
2087
2117
|
<CapRow className="template-footer-width">
|
|
@@ -2131,6 +2161,7 @@ Creatives.propTypes = {
|
|
|
2131
2161
|
hostName: PropTypes.string,
|
|
2132
2162
|
eventContextTags: PropTypes.array,
|
|
2133
2163
|
loyaltyTagFetchingDependencies: PropTypes.object,
|
|
2164
|
+
customerType: PropTypes.string,
|
|
2134
2165
|
intl: PropTypes.shape({
|
|
2135
2166
|
formatMessage: PropTypes.func,
|
|
2136
2167
|
}),
|
|
@@ -378,4 +378,16 @@ export default defineMessages({
|
|
|
378
378
|
id: `${scope}.next`,
|
|
379
379
|
defaultMessage: `Next`,
|
|
380
380
|
},
|
|
381
|
+
"channelNotSupportedAnonymous": {
|
|
382
|
+
id: `${scope}.channelNotSupportedAnonymous`,
|
|
383
|
+
defaultMessage: `This channel is not supported for anonymous customers`,
|
|
384
|
+
},
|
|
385
|
+
"personalizationNotSupportedAnonymous": {
|
|
386
|
+
id: `${scope}.personalizationNotSupportedAnonymous`,
|
|
387
|
+
defaultMessage: `Personalization tags are not supported for anonymous customers`,
|
|
388
|
+
},
|
|
389
|
+
"personalizationTokensErrorMessage": {
|
|
390
|
+
id: `${scope}.personalizationTokensErrorMessage`,
|
|
391
|
+
defaultMessage: `Personalization tags are not supported for anonymous customers. Please remove the tags.`,
|
|
392
|
+
},
|
|
381
393
|
});
|