@capillarytech/creatives-library 8.0.200-alpha.0 → 8.0.200
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 -0
- package/package.json +1 -1
- package/utils/common.js +7 -1
- package/utils/createMobilePushPayload.js +6 -27
- package/utils/tests/createMobilePushPayload.test.js +0 -53
- package/v2Containers/CreativesContainer/SlideBoxContent.js +6 -2
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +0 -135
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -145
- package/v2Containers/MobilePushNew/index.js +70 -172
- package/v2Containers/MobilePushNew/messages.js +0 -17
- package/v2Containers/Templates/index.js +15 -11
package/constants/unified.js
CHANGED
|
@@ -44,6 +44,7 @@ export const REGISTRATION_CUSTOM_FIELD = 'Registration custom fields';
|
|
|
44
44
|
export const GIFT_CARDS = 'GIFT_CARDS';
|
|
45
45
|
export const PROMO_ENGINE = 'PROMO_ENGINE';
|
|
46
46
|
export const LIQUID_SUPPORT = 'ENABLE_LIQUID_SUPPORT';
|
|
47
|
+
export const ENABLE_NEW_MPUSH = 'ENABLE_NEW_MPUSH';
|
|
47
48
|
export const CUSTOM_TAG = 'CustomTagMessage';
|
|
48
49
|
export const CUSTOMER_EXTENDED_FIELD = 'Customer extended fields';
|
|
49
50
|
export const EXTENDED_TAG = 'ExtendedTagMessage';
|
package/package.json
CHANGED
package/utils/common.js
CHANGED
|
@@ -21,7 +21,8 @@ import {
|
|
|
21
21
|
EMAIL_UNSUBSCRIBE_TAG_MANDATORY,
|
|
22
22
|
BADGES_ISSUE,
|
|
23
23
|
ENABLE_WECHAT,
|
|
24
|
-
LIQUID_SUPPORT
|
|
24
|
+
LIQUID_SUPPORT,
|
|
25
|
+
ENABLE_NEW_MPUSH
|
|
25
26
|
} from '../constants/unified';
|
|
26
27
|
import { apiMessageFormatHandler } from './commonUtils';
|
|
27
28
|
|
|
@@ -125,6 +126,11 @@ export const isEmailUnsubscribeTagMandatory = Auth.hasFeatureAccess.bind(
|
|
|
125
126
|
EMAIL_UNSUBSCRIBE_TAG_MANDATORY,
|
|
126
127
|
);
|
|
127
128
|
|
|
129
|
+
export const hasNewMobilePushFeatureEnabled = Auth.hasFeatureAccess.bind(
|
|
130
|
+
null,
|
|
131
|
+
ENABLE_NEW_MPUSH,
|
|
132
|
+
);
|
|
133
|
+
|
|
128
134
|
//filtering tags based on scope
|
|
129
135
|
export const filterTags = (tagsToFilter, tagsList) => tagsList?.filter(
|
|
130
136
|
(tag) => !tagsToFilter?.includes(tag?.definition?.value)
|
|
@@ -62,33 +62,12 @@ const createMobilePushPayload = ({
|
|
|
62
62
|
throw new Error(intl.formatMessage(messages.templateNameEmptyError));
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
// Validate content
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
throw new Error(intl.formatMessage(messages.contentValidationError, { platform: 'Android' }));
|
|
72
|
-
}
|
|
73
|
-
if (isIosSupported && (!iosContent?.title || !iosContent?.message)) {
|
|
74
|
-
throw new Error(intl.formatMessage(messages.contentValidationError, { platform: 'iOS' }));
|
|
75
|
-
}
|
|
76
|
-
} else {
|
|
77
|
-
// Single platform mode: require at least one platform to have content
|
|
78
|
-
const hasAndroidContent = isAndroidSupported && androidContent?.title?.trim() && androidContent?.message?.trim();
|
|
79
|
-
const hasIosContent = isIosSupported && iosContent?.title?.trim() && iosContent?.message?.trim();
|
|
80
|
-
|
|
81
|
-
if (!hasAndroidContent && !hasIosContent) {
|
|
82
|
-
throw new Error(intl.formatMessage(messages.singlePlatformContentMissing));
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Validate individual platforms that are supported but have incomplete content
|
|
86
|
-
if (isAndroidSupported && !hasAndroidContent && androidContent) {
|
|
87
|
-
throw new Error(intl.formatMessage(messages.contentValidationError, { platform: 'Android' }));
|
|
88
|
-
}
|
|
89
|
-
if (isIosSupported && !hasIosContent && iosContent) {
|
|
90
|
-
throw new Error(intl.formatMessage(messages.contentValidationError, { platform: 'iOS' }));
|
|
91
|
-
}
|
|
65
|
+
// Validate content
|
|
66
|
+
if (isAndroidSupported && (!androidContent?.title || !androidContent?.message)) {
|
|
67
|
+
throw new Error(intl.formatMessage(messages.contentValidationError, { platform: 'Android' }));
|
|
68
|
+
}
|
|
69
|
+
if (isIosSupported && (!iosContent?.title || !iosContent?.message)) {
|
|
70
|
+
throw new Error(intl.formatMessage(messages.contentValidationError, { platform: 'iOS' }));
|
|
92
71
|
}
|
|
93
72
|
|
|
94
73
|
// Ensure imageSrc has the required properties
|
|
@@ -261,59 +261,6 @@ describe('createMobilePushPayload', () => {
|
|
|
261
261
|
accountData: unsupportedAccountData,
|
|
262
262
|
})).not.toThrow();
|
|
263
263
|
});
|
|
264
|
-
|
|
265
|
-
// Single Platform Mode Tests
|
|
266
|
-
it('should allow single platform when allowSinglePlatform is true - Android only', () => {
|
|
267
|
-
expect(() => callWithIntl({
|
|
268
|
-
templateName: 'Test',
|
|
269
|
-
androidContent: { title: 'Title', message: 'Message' },
|
|
270
|
-
iosContent: null,
|
|
271
|
-
accountData: mockAccountData,
|
|
272
|
-
options: { allowSinglePlatform: true },
|
|
273
|
-
})).not.toThrow();
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
it('should allow single platform when allowSinglePlatform is true - iOS only', () => {
|
|
277
|
-
expect(() => callWithIntl({
|
|
278
|
-
templateName: 'Test',
|
|
279
|
-
androidContent: null,
|
|
280
|
-
iosContent: { title: 'Title', message: 'Message' },
|
|
281
|
-
accountData: mockAccountData,
|
|
282
|
-
options: { allowSinglePlatform: true },
|
|
283
|
-
})).not.toThrow();
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
it('should throw error when no platforms have content even with allowSinglePlatform', () => {
|
|
287
|
-
expect(() => callWithIntl({
|
|
288
|
-
templateName: 'Test',
|
|
289
|
-
androidContent: null,
|
|
290
|
-
iosContent: null,
|
|
291
|
-
accountData: mockAccountData,
|
|
292
|
-
options: { allowSinglePlatform: true },
|
|
293
|
-
})).toThrow(intl.formatMessage(messages.singlePlatformContentMissing));
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
it('should validate individual platforms in single platform mode - Android incomplete', () => {
|
|
297
|
-
// Test case where iOS has valid content but Android has incomplete content
|
|
298
|
-
expect(() => callWithIntl({
|
|
299
|
-
templateName: 'Test',
|
|
300
|
-
androidContent: { title: 'Valid Title' }, // Missing message property
|
|
301
|
-
iosContent: { title: 'Valid Title', message: 'Valid Message' }, // Complete content
|
|
302
|
-
accountData: mockAccountData,
|
|
303
|
-
options: { allowSinglePlatform: true },
|
|
304
|
-
})).toThrow(intl.formatMessage(messages.contentValidationError, { platform: 'Android' }));
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
it('should validate individual platforms in single platform mode - iOS incomplete', () => {
|
|
308
|
-
// Test case where Android has valid content but iOS has incomplete content
|
|
309
|
-
expect(() => callWithIntl({
|
|
310
|
-
templateName: 'Test',
|
|
311
|
-
androidContent: { title: 'Valid Title', message: 'Valid Message' }, // Complete content
|
|
312
|
-
iosContent: { title: 'Valid Title' }, // Missing message property
|
|
313
|
-
accountData: mockAccountData,
|
|
314
|
-
options: { allowSinglePlatform: true },
|
|
315
|
-
})).toThrow(intl.formatMessage(messages.contentValidationError, { platform: 'iOS' }));
|
|
316
|
-
});
|
|
317
264
|
});
|
|
318
265
|
|
|
319
266
|
// Account Data Validation Tests
|
|
@@ -668,7 +668,9 @@ export function SlideBoxContent(props) {
|
|
|
668
668
|
/>
|
|
669
669
|
)}
|
|
670
670
|
{isEditMPush && (
|
|
671
|
-
(
|
|
671
|
+
(isFullMode && !commonUtil.hasNewMobilePushFeatureEnabled()) ||
|
|
672
|
+
(!isFullMode && isLoyaltyModule) ||
|
|
673
|
+
(!isFullMode && !isLoyaltyModule && !commonUtil.hasNewMobilePushFeatureEnabled()) ? (
|
|
672
674
|
<MobliPushEdit
|
|
673
675
|
getFormLibraryData={getFormData}
|
|
674
676
|
setIsLoadingContent={setIsLoadingContent}
|
|
@@ -724,7 +726,9 @@ export function SlideBoxContent(props) {
|
|
|
724
726
|
)
|
|
725
727
|
)}
|
|
726
728
|
{isCreateMPush && (
|
|
727
|
-
(
|
|
729
|
+
(isFullMode && !commonUtil.hasNewMobilePushFeatureEnabled()) ||
|
|
730
|
+
(!isFullMode && isLoyaltyModule) ||
|
|
731
|
+
(!isFullMode && !isLoyaltyModule && !commonUtil.hasNewMobilePushFeatureEnabled()) ? (
|
|
728
732
|
<MobilepushWrapper
|
|
729
733
|
key="creatives-mobilepush-wrapper"
|
|
730
734
|
date={new Date().getMilliseconds()}
|
|
@@ -700,139 +700,4 @@ describe('Test SlideBoxContent container', () => {
|
|
|
700
700
|
expect(renderedComponent).toMatchSnapshot();
|
|
701
701
|
});
|
|
702
702
|
});
|
|
703
|
-
|
|
704
|
-
// Mobile Push Loyalty Module Tests
|
|
705
|
-
describe('Mobile Push with Loyalty Module', () => {
|
|
706
|
-
it('Should render MobliPushEdit for loyalty module in library mode (edit)', () => {
|
|
707
|
-
renderFunction('MOBILE_PUSH', 'editTemplate', { _id: 'test-id' }, {
|
|
708
|
-
isFullMode: false,
|
|
709
|
-
isLoyaltyModule: true
|
|
710
|
-
});
|
|
711
|
-
expect(renderedComponent).toMatchSnapshot();
|
|
712
|
-
});
|
|
713
|
-
|
|
714
|
-
it('Should render MobilePushNew for loyalty module in full mode (edit)', () => {
|
|
715
|
-
renderFunction('MOBILE_PUSH', 'editTemplate', { _id: 'test-id' }, {
|
|
716
|
-
isFullMode: true,
|
|
717
|
-
isLoyaltyModule: true
|
|
718
|
-
});
|
|
719
|
-
expect(renderedComponent).toMatchSnapshot();
|
|
720
|
-
});
|
|
721
|
-
|
|
722
|
-
it('Should render MobilepushWrapper for loyalty module in library mode (create)', () => {
|
|
723
|
-
renderFunction('MOBILE_PUSH', 'createTemplate', { mode: 'create' }, {
|
|
724
|
-
isFullMode: false,
|
|
725
|
-
isLoyaltyModule: true
|
|
726
|
-
});
|
|
727
|
-
expect(renderedComponent).toMatchSnapshot();
|
|
728
|
-
});
|
|
729
|
-
|
|
730
|
-
it('Should render MobilePushNew for loyalty module in full mode (create)', () => {
|
|
731
|
-
renderFunction('MOBILE_PUSH', 'createTemplate', { mode: 'create' }, {
|
|
732
|
-
isFullMode: true,
|
|
733
|
-
isLoyaltyModule: true
|
|
734
|
-
});
|
|
735
|
-
expect(renderedComponent).toMatchSnapshot();
|
|
736
|
-
});
|
|
737
|
-
|
|
738
|
-
it('Should render MobilePushNew for non-loyalty module in library mode (edit)', () => {
|
|
739
|
-
renderFunction('MOBILE_PUSH', 'editTemplate', { _id: 'test-id' }, {
|
|
740
|
-
isFullMode: false,
|
|
741
|
-
isLoyaltyModule: false
|
|
742
|
-
});
|
|
743
|
-
expect(renderedComponent).toMatchSnapshot();
|
|
744
|
-
});
|
|
745
|
-
|
|
746
|
-
it('Should render MobilePushNew for non-loyalty module in library mode (create)', () => {
|
|
747
|
-
renderFunction('MOBILE_PUSH', 'createTemplate', { mode: 'create' }, {
|
|
748
|
-
isFullMode: false,
|
|
749
|
-
isLoyaltyModule: false
|
|
750
|
-
});
|
|
751
|
-
expect(renderedComponent).toMatchSnapshot();
|
|
752
|
-
});
|
|
753
|
-
});
|
|
754
|
-
|
|
755
|
-
// Additional edge case tests to cover uncovered lines
|
|
756
|
-
describe('Edge Cases for Uncovered Lines', () => {
|
|
757
|
-
it('Should handle getLineType with LINE channel and empty templateData', () => {
|
|
758
|
-
// This should cover lines 72-74 in getLineType function
|
|
759
|
-
// LINE channel, not full mode, templateData exists but has no _id, no messageBody, and not isDefault
|
|
760
|
-
const emptyLineTemplateData = {
|
|
761
|
-
type: 'LINE',
|
|
762
|
-
isDefault: false,
|
|
763
|
-
// No _id and no versions.base.content.messages[0]
|
|
764
|
-
};
|
|
765
|
-
renderFunction('LINE', 'editTemplate', emptyLineTemplateData, {
|
|
766
|
-
isFullMode: false
|
|
767
|
-
});
|
|
768
|
-
expect(renderedComponent).toMatchSnapshot();
|
|
769
|
-
});
|
|
770
|
-
|
|
771
|
-
it('Should handle SMS channel with DLT content processing', () => {
|
|
772
|
-
// This should cover line 251 in SMS content processing
|
|
773
|
-
// Need isDltEnabled = true and updatedSmsEditor as array
|
|
774
|
-
// Mock hasTraiDltFeature to return true
|
|
775
|
-
const commonUtils = require('../../../utils/common');
|
|
776
|
-
const originalHasTraiDltFeature = commonUtils.hasTraiDltFeature;
|
|
777
|
-
commonUtils.hasTraiDltFeature = jest.fn(() => true);
|
|
778
|
-
|
|
779
|
-
const smsTemplateData = {
|
|
780
|
-
type: 'SMS',
|
|
781
|
-
versions: {
|
|
782
|
-
base: {
|
|
783
|
-
'updated-sms-editor': ['Line 1', 'Line 2', 'Line 3'], // Array to trigger join()
|
|
784
|
-
'sms-editor': 'Fallback SMS content'
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
};
|
|
788
|
-
renderFunction('SMS', 'preview', smsTemplateData, {
|
|
789
|
-
smsRegister: 'DLT', // Enable DLT for library mode
|
|
790
|
-
isFullMode: false
|
|
791
|
-
});
|
|
792
|
-
|
|
793
|
-
// Restore original function
|
|
794
|
-
commonUtils.hasTraiDltFeature = originalHasTraiDltFeature;
|
|
795
|
-
expect(renderedComponent).toMatchSnapshot();
|
|
796
|
-
});
|
|
797
|
-
|
|
798
|
-
it('Should handle LINE channel with video content', () => {
|
|
799
|
-
// This should cover lines 314-315 for video detection
|
|
800
|
-
// Need a LINE message with video property but no text, previewImageUrl, selectedSticker, baseUrl, template, or contents
|
|
801
|
-
const lineTemplateDataWithVideo = {
|
|
802
|
-
type: 'LINE',
|
|
803
|
-
versions: {
|
|
804
|
-
base: {
|
|
805
|
-
content: {
|
|
806
|
-
messages: [{
|
|
807
|
-
video: {
|
|
808
|
-
originalContentUrl: 'https://example.com/video.mp4',
|
|
809
|
-
externalLink: {
|
|
810
|
-
linkUri: 'https://example.com/link'
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
// No text, previewImageUrl, selectedSticker, baseUrl, template, or contents
|
|
814
|
-
}]
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
};
|
|
819
|
-
renderFunction('LINE', 'preview', lineTemplateDataWithVideo);
|
|
820
|
-
expect(renderedComponent).toMatchSnapshot();
|
|
821
|
-
});
|
|
822
|
-
|
|
823
|
-
it('Should handle default case in getChannelPreviewContent', () => {
|
|
824
|
-
// This should cover line 395 - default case return
|
|
825
|
-
const unknownChannelData = {
|
|
826
|
-
versions: {
|
|
827
|
-
base: {
|
|
828
|
-
content: {
|
|
829
|
-
customField: 'custom content'
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
};
|
|
834
|
-
renderFunction('UNKNOWN_CHANNEL', 'preview', unknownChannelData);
|
|
835
|
-
expect(renderedComponent).toMatchSnapshot();
|
|
836
|
-
});
|
|
837
|
-
});
|
|
838
703
|
});
|
|
@@ -1,138 +1,5 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
|
-
exports[`Test SlideBoxContent container Edge Cases for Uncovered Lines Should handle LINE channel with video content 1`] = `
|
|
4
|
-
<SlideBoxContent__CreativesWrapper>
|
|
5
|
-
<InjectIntl(TemplatePreview)
|
|
6
|
-
channel="line"
|
|
7
|
-
charCounterEnabled={false}
|
|
8
|
-
content={
|
|
9
|
-
Array [
|
|
10
|
-
Object {
|
|
11
|
-
"actionUrl": "https://example.com/link",
|
|
12
|
-
"baseUrl": undefined,
|
|
13
|
-
"imageCarousel": Array [
|
|
14
|
-
Object {
|
|
15
|
-
"actionLabel": undefined,
|
|
16
|
-
"imageUrl": undefined,
|
|
17
|
-
},
|
|
18
|
-
],
|
|
19
|
-
"messageContent": undefined,
|
|
20
|
-
"previewImageUrl": undefined,
|
|
21
|
-
"selectedSticker": Object {
|
|
22
|
-
"animatedStickerUrl": undefined,
|
|
23
|
-
"packageId": undefined,
|
|
24
|
-
"stickerId": undefined,
|
|
25
|
-
"stickerUrl": undefined,
|
|
26
|
-
},
|
|
27
|
-
"type": "image_carousel",
|
|
28
|
-
"video": "https://example.com/video.mp4",
|
|
29
|
-
},
|
|
30
|
-
]
|
|
31
|
-
}
|
|
32
|
-
showCount={true}
|
|
33
|
-
templateData={
|
|
34
|
-
Object {
|
|
35
|
-
"type": "LINE",
|
|
36
|
-
"versions": Object {
|
|
37
|
-
"base": Object {
|
|
38
|
-
"content": Object {
|
|
39
|
-
"messages": Array [
|
|
40
|
-
Object {
|
|
41
|
-
"video": Object {
|
|
42
|
-
"externalLink": Object {
|
|
43
|
-
"linkUri": "https://example.com/link",
|
|
44
|
-
},
|
|
45
|
-
"originalContentUrl": "https://example.com/video.mp4",
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
],
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
viberBrandName=""
|
|
55
|
-
/>
|
|
56
|
-
<Connect(Connect(UserIsAuthenticated(InjectIntl(CreativesCommon))))
|
|
57
|
-
location={
|
|
58
|
-
Object {
|
|
59
|
-
"pathname": "/sms/edit",
|
|
60
|
-
"query": Object {
|
|
61
|
-
"isEditFromCampaigns": undefined,
|
|
62
|
-
"module": "library",
|
|
63
|
-
"type": "embedded",
|
|
64
|
-
},
|
|
65
|
-
"search": "",
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
onCreateComplete={[MockFunction]}
|
|
69
|
-
params={
|
|
70
|
-
Object {
|
|
71
|
-
"id": undefined,
|
|
72
|
-
"mode": undefined,
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
route={
|
|
76
|
-
Object {
|
|
77
|
-
"name": "edit_text",
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
templateData={
|
|
81
|
-
Object {
|
|
82
|
-
"type": "LINE",
|
|
83
|
-
"versions": Object {
|
|
84
|
-
"base": Object {
|
|
85
|
-
"content": Object {
|
|
86
|
-
"messages": Array [
|
|
87
|
-
Object {
|
|
88
|
-
"video": Object {
|
|
89
|
-
"externalLink": Object {
|
|
90
|
-
"linkUri": "https://example.com/link",
|
|
91
|
-
},
|
|
92
|
-
"originalContentUrl": "https://example.com/video.mp4",
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
],
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
/>
|
|
102
|
-
</SlideBoxContent__CreativesWrapper>
|
|
103
|
-
`;
|
|
104
|
-
|
|
105
|
-
exports[`Test SlideBoxContent container Edge Cases for Uncovered Lines Should handle SMS channel with DLT content processing 1`] = `
|
|
106
|
-
<SlideBoxContent__CreativesWrapper>
|
|
107
|
-
<InjectIntl(TemplatePreview)
|
|
108
|
-
channel="sms"
|
|
109
|
-
charCounterEnabled={true}
|
|
110
|
-
content="Fallback SMS content"
|
|
111
|
-
showCount={true}
|
|
112
|
-
templateData={
|
|
113
|
-
Object {
|
|
114
|
-
"type": "SMS",
|
|
115
|
-
"versions": Object {
|
|
116
|
-
"base": Object {
|
|
117
|
-
"sms-editor": "Fallback SMS content",
|
|
118
|
-
"updated-sms-editor": Array [
|
|
119
|
-
"Line 1",
|
|
120
|
-
"Line 2",
|
|
121
|
-
"Line 3",
|
|
122
|
-
],
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
viberBrandName=""
|
|
128
|
-
/>
|
|
129
|
-
</SlideBoxContent__CreativesWrapper>
|
|
130
|
-
`;
|
|
131
|
-
|
|
132
|
-
exports[`Test SlideBoxContent container Edge Cases for Uncovered Lines Should handle default case in getChannelPreviewContent 1`] = `<SlideBoxContent__CreativesWrapper />`;
|
|
133
|
-
|
|
134
|
-
exports[`Test SlideBoxContent container Edge Cases for Uncovered Lines Should handle getLineType with LINE channel and empty templateData 1`] = `<SlideBoxContent__CreativesWrapper />`;
|
|
135
|
-
|
|
136
3
|
exports[`Test SlideBoxContent container Email component isTestAndPreviewMode IIFE Should handle isTestAndPreviewMode IIFE in Email edit mode with ID 1`] = `
|
|
137
4
|
<SlideBoxContent__CreativesWrapper>
|
|
138
5
|
<ForwardRef
|
|
@@ -335,18 +202,6 @@ exports[`Test SlideBoxContent container Email component isTestAndPreviewMode IIF
|
|
|
335
202
|
</SlideBoxContent__CreativesWrapper>
|
|
336
203
|
`;
|
|
337
204
|
|
|
338
|
-
exports[`Test SlideBoxContent container Mobile Push with Loyalty Module Should render MobilePushNew for loyalty module in full mode (create) 1`] = `<SlideBoxContent__CreativesWrapper />`;
|
|
339
|
-
|
|
340
|
-
exports[`Test SlideBoxContent container Mobile Push with Loyalty Module Should render MobilePushNew for loyalty module in full mode (edit) 1`] = `<SlideBoxContent__CreativesWrapper />`;
|
|
341
|
-
|
|
342
|
-
exports[`Test SlideBoxContent container Mobile Push with Loyalty Module Should render MobilePushNew for non-loyalty module in library mode (create) 1`] = `<SlideBoxContent__CreativesWrapper />`;
|
|
343
|
-
|
|
344
|
-
exports[`Test SlideBoxContent container Mobile Push with Loyalty Module Should render MobilePushNew for non-loyalty module in library mode (edit) 1`] = `<SlideBoxContent__CreativesWrapper />`;
|
|
345
|
-
|
|
346
|
-
exports[`Test SlideBoxContent container Mobile Push with Loyalty Module Should render MobilepushWrapper for loyalty module in library mode (create) 1`] = `<SlideBoxContent__CreativesWrapper />`;
|
|
347
|
-
|
|
348
|
-
exports[`Test SlideBoxContent container Mobile Push with Loyalty Module Should render MobliPushEdit for loyalty module in library mode (edit) 1`] = `<SlideBoxContent__CreativesWrapper />`;
|
|
349
|
-
|
|
350
205
|
exports[`Test SlideBoxContent container Should handle isTestAndPreviewMode IIFE implementation correctly 1`] = `
|
|
351
206
|
<SlideBoxContent__CreativesWrapper>
|
|
352
207
|
<ForwardRef
|
|
@@ -21,7 +21,6 @@ import { intlShape } from "react-intl";
|
|
|
21
21
|
import "./index.scss";
|
|
22
22
|
import { GA } from "@capillarytech/cap-ui-utils";
|
|
23
23
|
import CapNotification from "@capillarytech/cap-ui-library/CapNotification";
|
|
24
|
-
import { CapModal } from "@capillarytech/cap-react-ui-library";
|
|
25
24
|
import { DAEMON } from "@capillarytech/vulcan-react-sdk/utils/sagaInjectorTypes";
|
|
26
25
|
import globalMessages from "../Cap/messages";
|
|
27
26
|
import * as actions from "./actions";
|
|
@@ -530,9 +529,6 @@ const MobilePushNew = ({
|
|
|
530
529
|
STANDARD_ERROR_MSG: {},
|
|
531
530
|
LIQUID_ERROR_MSG: {},
|
|
532
531
|
});
|
|
533
|
-
// Modal state for single platform confirmation
|
|
534
|
-
const [showModal, setShowModal] = useState(false);
|
|
535
|
-
const [modalContent, setModalContent] = useState({});
|
|
536
532
|
const [androidContent, setAndroidContent] = useState(INITIAL_CONTENT);
|
|
537
533
|
|
|
538
534
|
const [iosContent, setIosContent] = useState(INITIAL_CONTENT);
|
|
@@ -2134,93 +2130,39 @@ const MobilePushNew = ({
|
|
|
2134
2130
|
return panes;
|
|
2135
2131
|
}, [isAndroidSupported, isIosSupported, renderContentFields, formatMessage]);
|
|
2136
2132
|
|
|
2137
|
-
// Save button disabled logic:
|
|
2138
|
-
const hasAndroidData = isAndroidSupported && androidContent?.title?.trim() && androidContent?.message?.trim();
|
|
2139
|
-
const hasIosData = isIosSupported && iosContent?.title?.trim() && iosContent?.message?.trim();
|
|
2140
|
-
const hasAnyPlatformData = hasAndroidData || hasIosData;
|
|
2141
|
-
|
|
2142
|
-
// Validation checks for save button
|
|
2143
|
-
const carouselErrors = Object.values(carouselLinkErrors).some((error) => error !== null && error !== "");
|
|
2144
|
-
const carouselValid = isCarouselDataValid();
|
|
2145
|
-
|
|
2133
|
+
// Save button disabled logic: only check enabled platforms
|
|
2146
2134
|
const isSaveDisabled = (
|
|
2147
|
-
|
|
2148
|
-
|| (
|
|
2149
|
-
||
|
|
2150
|
-
||
|
|
2135
|
+
(isAndroidSupported && (!androidContent?.title?.trim() || !androidContent?.message?.trim()))
|
|
2136
|
+
|| (isIosSupported && (!iosContent?.title?.trim() || !iosContent?.message?.trim()))
|
|
2137
|
+
|| templateNameError
|
|
2138
|
+
|| Object.values(carouselLinkErrors).some((error) => error !== null && error !== "")
|
|
2139
|
+
|| !isCarouselDataValid()
|
|
2151
2140
|
);
|
|
2152
2141
|
|
|
2153
|
-
//
|
|
2154
|
-
const
|
|
2155
|
-
if (
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
};
|
|
2162
|
-
setModalContent(content);
|
|
2163
|
-
setShowModal(true);
|
|
2164
|
-
} else if (type === 'android') {
|
|
2165
|
-
const content = {
|
|
2166
|
-
title: formatMessage(messages.alertMessage),
|
|
2167
|
-
body: formatMessage(messages.androidTemplateNotConfigured),
|
|
2168
|
-
type: 'confirm',
|
|
2169
|
-
id: 'android',
|
|
2170
|
-
};
|
|
2171
|
-
setModalContent(content);
|
|
2172
|
-
setShowModal(true);
|
|
2142
|
+
// Validation in handleSave: only show errors for enabled platforms
|
|
2143
|
+
const handleSave = useCallback(() => {
|
|
2144
|
+
if (isAndroidSupported && (!androidContent?.title?.trim() || !androidContent?.message?.trim())) {
|
|
2145
|
+
CapNotification.error({
|
|
2146
|
+
message: formatMessage(messages.androidValidationError),
|
|
2147
|
+
});
|
|
2148
|
+
if (onValidationFail) onValidationFail();
|
|
2149
|
+
return;
|
|
2173
2150
|
}
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
// Internal save function with optional modal validation skip
|
|
2182
|
-
const handleSaveInternal = useCallback((skipModalValidation = false) => {
|
|
2183
|
-
// Check for single platform data and show modal confirmation (unless skipped)
|
|
2184
|
-
if (!skipModalValidation) {
|
|
2185
|
-
const androidHasData = androidContent?.title?.trim() && androidContent?.message?.trim();
|
|
2186
|
-
const iosHasData = iosContent?.title?.trim() && iosContent?.message?.trim();
|
|
2187
|
-
|
|
2188
|
-
// If both platforms are supported but only one has data, show confirmation modal
|
|
2189
|
-
if (isAndroidSupported && isIosSupported) {
|
|
2190
|
-
if (androidHasData && !iosHasData) {
|
|
2191
|
-
setModalContentHandler('ios'); // Show iOS not configured modal
|
|
2192
|
-
return;
|
|
2193
|
-
}
|
|
2194
|
-
if (iosHasData && !androidHasData) {
|
|
2195
|
-
setModalContentHandler('android'); // Show Android not configured modal
|
|
2196
|
-
return;
|
|
2197
|
-
}
|
|
2198
|
-
}
|
|
2151
|
+
if (isIosSupported && (!iosContent?.title?.trim() || !iosContent?.message?.trim())) {
|
|
2152
|
+
CapNotification.error({
|
|
2153
|
+
message: formatMessage(messages.iosValidationError),
|
|
2154
|
+
});
|
|
2155
|
+
if (onValidationFail) onValidationFail();
|
|
2156
|
+
return;
|
|
2199
2157
|
}
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
if (isAndroidSupported && (!androidContent?.title?.trim() || !androidContent?.message?.trim())) {
|
|
2206
|
-
CapNotification.error({
|
|
2207
|
-
message: formatMessage(messages.androidValidationError),
|
|
2208
|
-
});
|
|
2209
|
-
if (onValidationFail) onValidationFail();
|
|
2210
|
-
return;
|
|
2211
|
-
}
|
|
2212
|
-
if (isIosSupported && (!iosContent?.title?.trim() || !iosContent?.message?.trim())) {
|
|
2213
|
-
CapNotification.error({
|
|
2214
|
-
message: formatMessage(messages.iosValidationError),
|
|
2215
|
-
});
|
|
2216
|
-
if (onValidationFail) onValidationFail();
|
|
2217
|
-
return;
|
|
2218
|
-
}
|
|
2158
|
+
if (templateNameError) {
|
|
2159
|
+
CapNotification.error({
|
|
2160
|
+
message: formatMessage(messages.emptyTemplateErrorMessage),
|
|
2161
|
+
});
|
|
2162
|
+
return;
|
|
2219
2163
|
}
|
|
2220
|
-
//
|
|
2221
|
-
//
|
|
2222
|
-
|
|
2223
|
-
// Template name validation - only required in full mode
|
|
2164
|
+
// Only require templateName in full mode
|
|
2165
|
+
// Use ref to get the latest template name value for validation
|
|
2224
2166
|
const currentTemplateName = templateNameRef.current || templateName;
|
|
2225
2167
|
if (isFullMode && !currentTemplateName?.trim()) {
|
|
2226
2168
|
CapNotification.error({
|
|
@@ -2385,8 +2327,6 @@ const MobilePushNew = ({
|
|
|
2385
2327
|
}
|
|
2386
2328
|
|
|
2387
2329
|
// Create payload with enabled platform content and intl
|
|
2388
|
-
// In modal-confirmed flow, we may have intentionally missing platform data
|
|
2389
|
-
const allowSinglePlatform = skipModalValidation;
|
|
2390
2330
|
const payload = createMobilePushPayloadWithIntl({
|
|
2391
2331
|
templateName: finalTemplateName,
|
|
2392
2332
|
androidContent: isAndroidSupported ? processedAndroidContent : undefined,
|
|
@@ -2397,7 +2337,6 @@ const MobilePushNew = ({
|
|
|
2397
2337
|
sameContent,
|
|
2398
2338
|
options: {
|
|
2399
2339
|
mode: params?.mode,
|
|
2400
|
-
allowSinglePlatform,
|
|
2401
2340
|
},
|
|
2402
2341
|
intl,
|
|
2403
2342
|
});
|
|
@@ -2577,68 +2516,6 @@ const MobilePushNew = ({
|
|
|
2577
2516
|
isCarouselDataValid,
|
|
2578
2517
|
isFullMode,
|
|
2579
2518
|
templateId,
|
|
2580
|
-
activeTab,
|
|
2581
|
-
setModalContentHandler,
|
|
2582
|
-
]);
|
|
2583
|
-
|
|
2584
|
-
// Public save function (with modal validation)
|
|
2585
|
-
const handleSave = useCallback(() => {
|
|
2586
|
-
handleSaveInternal(false);
|
|
2587
|
-
}, [handleSaveInternal]);
|
|
2588
|
-
|
|
2589
|
-
// Save function that skips single platform modal validation
|
|
2590
|
-
const handleSaveWithoutModal = useCallback(() => {
|
|
2591
|
-
handleSaveInternal(true);
|
|
2592
|
-
}, [handleSaveInternal]);
|
|
2593
|
-
|
|
2594
|
-
const handleConfirmModal = useCallback(() => {
|
|
2595
|
-
setShowModal(false);
|
|
2596
|
-
setModalContent({});
|
|
2597
|
-
// Proceed with save after modal confirmation - skip single platform validation
|
|
2598
|
-
handleSaveWithoutModal();
|
|
2599
|
-
}, [handleSaveWithoutModal]);
|
|
2600
|
-
|
|
2601
|
-
const liquidMiddleWare = useCallback(() => {
|
|
2602
|
-
const onError = ({ standardErrors, liquidErrors }) => {
|
|
2603
|
-
setErrorMessage((prev) => ({
|
|
2604
|
-
STANDARD_ERROR_MSG: { ...prev.STANDARD_ERROR_MSG, ...standardErrors },
|
|
2605
|
-
LIQUID_ERROR_MSG: { ...prev.LIQUID_ERROR_MSG, ...liquidErrors },
|
|
2606
|
-
}));
|
|
2607
|
-
};
|
|
2608
|
-
const onSuccess = () => handleSave();
|
|
2609
|
-
|
|
2610
|
-
validateMobilePushContent([androidContent, iosContent], {
|
|
2611
|
-
currentTab: activeTab === ANDROID ? 1 : 2,
|
|
2612
|
-
onError,
|
|
2613
|
-
onSuccess,
|
|
2614
|
-
getLiquidTags: globalActionsProps.getLiquidTags,
|
|
2615
|
-
formatMessage,
|
|
2616
|
-
messages: formBuilderMessages,
|
|
2617
|
-
tagLookupMap: metaEntities?.tagLookupMap || {},
|
|
2618
|
-
eventContextTags: metaEntities?.eventContextTags || [],
|
|
2619
|
-
isLiquidFlow: hasLiquidSupportFeature(),
|
|
2620
|
-
forwardedTags: {},
|
|
2621
|
-
skipTags: (tag) => {
|
|
2622
|
-
const skipRegexes = [
|
|
2623
|
-
/dynamic_expiry_date_after_\d+_days\.FORMAT_\d/,
|
|
2624
|
-
/unsubscribe\(#[a-zA-Z\d]{6}\)/,
|
|
2625
|
-
/Link_to_[a-zA-z]/,
|
|
2626
|
-
/SURVEY.*\.TOKEN/,
|
|
2627
|
-
/^[A-Za-z].*\([a-zA-Z\d]*\)/,
|
|
2628
|
-
];
|
|
2629
|
-
return skipRegexes.some((regex) => regex.test(tag));
|
|
2630
|
-
},
|
|
2631
|
-
singleTab: getSingleTab(accountData),
|
|
2632
|
-
});
|
|
2633
|
-
}, [
|
|
2634
|
-
androidContent,
|
|
2635
|
-
iosContent,
|
|
2636
|
-
activeTab,
|
|
2637
|
-
globalActionsProps,
|
|
2638
|
-
formatMessage,
|
|
2639
|
-
metaEntities,
|
|
2640
|
-
accountData,
|
|
2641
|
-
handleSave,
|
|
2642
2519
|
]);
|
|
2643
2520
|
|
|
2644
2521
|
// Helper to sync content between platforms
|
|
@@ -2670,9 +2547,7 @@ const MobilePushNew = ({
|
|
|
2670
2547
|
setTemplateName(value);
|
|
2671
2548
|
// Update ref to always have the latest value
|
|
2672
2549
|
templateNameRef.current = value;
|
|
2673
|
-
|
|
2674
|
-
// In full mode, template name is required only at save time, not during typing
|
|
2675
|
-
const isInvalid = isFullMode && value.trim() === "";
|
|
2550
|
+
const isInvalid = !value || value.trim() === "";
|
|
2676
2551
|
setTemplateNameError(isInvalid);
|
|
2677
2552
|
if (value && onEnterTemplateName) {
|
|
2678
2553
|
onEnterTemplateName();
|
|
@@ -2680,7 +2555,7 @@ const MobilePushNew = ({
|
|
|
2680
2555
|
onRemoveTemplateName();
|
|
2681
2556
|
}
|
|
2682
2557
|
},
|
|
2683
|
-
[onEnterTemplateName, onRemoveTemplateName
|
|
2558
|
+
[onEnterTemplateName, onRemoveTemplateName]
|
|
2684
2559
|
);
|
|
2685
2560
|
|
|
2686
2561
|
// --- Only show template name input in full mode (not library/consumer mode) ---
|
|
@@ -2709,6 +2584,47 @@ const MobilePushNew = ({
|
|
|
2709
2584
|
[isFullMode, formatMessage, templateName, onTemplateNameChange, templateNameError, params?.id]
|
|
2710
2585
|
);
|
|
2711
2586
|
|
|
2587
|
+
const liquidMiddleWare = useCallback(() => {
|
|
2588
|
+
const onError = ({ standardErrors, liquidErrors }) => {
|
|
2589
|
+
setErrorMessage((prev) => ({
|
|
2590
|
+
STANDARD_ERROR_MSG: { ...prev.STANDARD_ERROR_MSG, ...standardErrors },
|
|
2591
|
+
LIQUID_ERROR_MSG: { ...prev.LIQUID_ERROR_MSG, ...liquidErrors },
|
|
2592
|
+
}));
|
|
2593
|
+
};
|
|
2594
|
+
const onSuccess = () => handleSave();
|
|
2595
|
+
|
|
2596
|
+
validateMobilePushContent([androidContent, iosContent], {
|
|
2597
|
+
currentTab: activeTab === ANDROID ? 1 : 2,
|
|
2598
|
+
onError,
|
|
2599
|
+
onSuccess,
|
|
2600
|
+
getLiquidTags: globalActionsProps.getLiquidTags,
|
|
2601
|
+
formatMessage,
|
|
2602
|
+
messages: formBuilderMessages,
|
|
2603
|
+
tagLookupMap: metaEntities?.tagLookupMap || {},
|
|
2604
|
+
eventContextTags: metaEntities?.eventContextTags || [],
|
|
2605
|
+
isLiquidFlow: hasLiquidSupportFeature(),
|
|
2606
|
+
forwardedTags: {},
|
|
2607
|
+
skipTags: (tag) => {
|
|
2608
|
+
const skipRegexes = [
|
|
2609
|
+
/dynamic_expiry_date_after_\d+_days\.FORMAT_\d/,
|
|
2610
|
+
/unsubscribe\(#[a-zA-Z\d]{6}\)/,
|
|
2611
|
+
/Link_to_[a-zA-z]/,
|
|
2612
|
+
/SURVEY.*\.TOKEN/,
|
|
2613
|
+
/^[A-Za-z].*\([a-zA-Z\d]*\)/,
|
|
2614
|
+
];
|
|
2615
|
+
return skipRegexes.some((regex) => regex.test(tag));
|
|
2616
|
+
},
|
|
2617
|
+
singleTab: getSingleTab(accountData),
|
|
2618
|
+
});
|
|
2619
|
+
}, [
|
|
2620
|
+
androidContent,
|
|
2621
|
+
iosContent,
|
|
2622
|
+
activeTab,
|
|
2623
|
+
globalActionsProps,
|
|
2624
|
+
formatMessage,
|
|
2625
|
+
metaEntities,
|
|
2626
|
+
accountData,
|
|
2627
|
+
]);
|
|
2712
2628
|
|
|
2713
2629
|
const isLiquidFlow = hasLiquidSupportFeature();
|
|
2714
2630
|
|
|
@@ -3029,24 +2945,6 @@ const MobilePushNew = ({
|
|
|
3029
2945
|
/>
|
|
3030
2946
|
</CapColumn>
|
|
3031
2947
|
</CapRow>
|
|
3032
|
-
|
|
3033
|
-
{/* Modal for single platform confirmation */}
|
|
3034
|
-
<CapModal
|
|
3035
|
-
visible={showModal}
|
|
3036
|
-
title={modalContent.title}
|
|
3037
|
-
onOk={handleConfirmModal}
|
|
3038
|
-
onCancel={handleCancelModal}
|
|
3039
|
-
footer={[
|
|
3040
|
-
<CapButton key="cancel" onClick={handleCancelModal}>
|
|
3041
|
-
No
|
|
3042
|
-
</CapButton>,
|
|
3043
|
-
<CapButton key="confirm" type="primary" onClick={handleConfirmModal}>
|
|
3044
|
-
Yes
|
|
3045
|
-
</CapButton>,
|
|
3046
|
-
]}
|
|
3047
|
-
>
|
|
3048
|
-
{modalContent.body}
|
|
3049
|
-
</CapModal>
|
|
3050
2948
|
</CapSpin>
|
|
3051
2949
|
);
|
|
3052
2950
|
};
|
|
@@ -252,10 +252,6 @@ export default defineMessages({
|
|
|
252
252
|
id: `${scope}.contentValidationError`,
|
|
253
253
|
defaultMessage: '{platform} content must have title and message',
|
|
254
254
|
},
|
|
255
|
-
singlePlatformContentMissing: {
|
|
256
|
-
id: `${scope}.singlePlatformContentMissing`,
|
|
257
|
-
defaultMessage: 'At least one platform must have title and message',
|
|
258
|
-
},
|
|
259
255
|
// File validation error messages for useUpload.js
|
|
260
256
|
fileSizeError: {
|
|
261
257
|
id: `${scope}.fileSizeError`,
|
|
@@ -273,17 +269,4 @@ export default defineMessages({
|
|
|
273
269
|
id: `${scope}.gifFileTypeError`,
|
|
274
270
|
defaultMessage: 'Only GIF files are allowed',
|
|
275
271
|
},
|
|
276
|
-
// Modal confirmation messages for single platform data
|
|
277
|
-
alertMessage: {
|
|
278
|
-
id: `${scope}.alertMessage`,
|
|
279
|
-
defaultMessage: 'Alert',
|
|
280
|
-
},
|
|
281
|
-
androidTemplateNotConfigured: {
|
|
282
|
-
id: `${scope}.androidTemplateNotConfigured`,
|
|
283
|
-
defaultMessage: 'Android template is not configured. Continue save?',
|
|
284
|
-
},
|
|
285
|
-
iosTemplateNotConfigured: {
|
|
286
|
-
id: `${scope}.iosTemplateNotConfigured`,
|
|
287
|
-
defaultMessage: 'IOS template is not configured, Save without IOS template',
|
|
288
|
-
},
|
|
289
272
|
});
|
|
@@ -1357,16 +1357,19 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
1357
1357
|
break;
|
|
1358
1358
|
}
|
|
1359
1359
|
case MOBILE_PUSH:
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1360
|
+
if (!commonUtil.hasNewMobilePushFeatureEnabled()) {
|
|
1361
|
+
templateData.content = template;
|
|
1362
|
+
} else {
|
|
1363
|
+
const mpushData = get(template, 'versions.base', template);
|
|
1364
|
+
const androidData = get(mpushData, 'ANDROID') || get(mpushData, 'androidContent');
|
|
1365
|
+
const iosData = get(mpushData, 'IOS') || get(mpushData, 'iosContent');
|
|
1366
|
+
let mpushListingData = androidData;
|
|
1367
|
+
if (isEmpty(androidData) || !androidData?.title) {
|
|
1368
|
+
mpushListingData = iosData;
|
|
1369
|
+
};
|
|
1370
|
+
const { title, message, expandableDetails: { style = '', image, carouselData = [], ctas = [], media = [] } = {} } = mpushListingData || {};
|
|
1371
|
+
const {url = ''} = media?.[0] || {};
|
|
1372
|
+
templateData.content = (
|
|
1370
1373
|
<div className='mobilepush-container'>
|
|
1371
1374
|
<div className="app-header">
|
|
1372
1375
|
<div className="app-header-left">
|
|
@@ -1438,7 +1441,8 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
1438
1441
|
)}
|
|
1439
1442
|
</div>
|
|
1440
1443
|
);
|
|
1441
|
-
|
|
1444
|
+
templateData.isNewMobilePush = commonUtil.hasNewMobilePushFeatureEnabled();
|
|
1445
|
+
}
|
|
1442
1446
|
break;
|
|
1443
1447
|
case INAPP:
|
|
1444
1448
|
templateData.content = template;
|