@capillarytech/creatives-library 7.17.97-alpha.0 → 7.17.98
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/v2Components/CapDeviceContent/index.js +14 -1
- package/v2Components/CapDeviceContent/index.scss +1 -1
- package/v2Components/CapDeviceContent/tests/index.test.js +3 -1
- package/v2Components/CapInAppCTA/constants.js +3 -3
- package/v2Components/CapInAppCTA/index.js +3 -3
- package/v2Components/FormBuilder/index.js +1 -1
- package/v2Components/MobilePushPreviewV2/index.js +3 -24
- package/v2Components/TemplatePreview/_templatePreview.scss +4 -4
- package/v2Containers/CreativesContainer/SlideBoxContent.js +3 -0
- package/v2Containers/CreativesContainer/index.js +6 -4
- package/v2Containers/Email/index.js +1 -7
- package/v2Containers/InApp/constants.js +35 -0
- package/v2Containers/InApp/index.js +102 -76
- package/v2Containers/InApp/index.scss +3 -1
- package/v2Containers/InApp/messages.js +15 -0
- package/v2Containers/InApp/tests/index.test.js +21 -0
- package/v2Containers/InApp/tests/mockData.js +6 -6
- package/v2Containers/InApp/utils.js +12 -0
- package/v2Containers/Templates/index.js +70 -17
package/package.json
CHANGED
|
@@ -65,6 +65,9 @@ const CapDeviceContent = (props) => {
|
|
|
65
65
|
tags,
|
|
66
66
|
onTagSelect,
|
|
67
67
|
handleOnTagsContextChange,
|
|
68
|
+
templateDescErrorHandler,
|
|
69
|
+
templateTitleError,
|
|
70
|
+
setTemplateTitleError,
|
|
68
71
|
} = props || {};
|
|
69
72
|
const { TextArea } = CapInput;
|
|
70
73
|
const { formatMessage } = intl;
|
|
@@ -73,7 +76,7 @@ const CapDeviceContent = (props) => {
|
|
|
73
76
|
const isMediaTypeImage = INAPP_MEDIA_TYPES.IMAGE === templateMediaType;
|
|
74
77
|
const isBtnTypeCta = buttonType === INAPP_BUTTON_TYPES.CTA;
|
|
75
78
|
const onTemplateMessageChange = ({ target: { value } }) => {
|
|
76
|
-
let error =
|
|
79
|
+
let error = templateDescErrorHandler(value);
|
|
77
80
|
if (value === '') {
|
|
78
81
|
error = formatMessage(messages.emptyTemplateMessageErrorMessage);
|
|
79
82
|
}
|
|
@@ -118,7 +121,12 @@ const CapDeviceContent = (props) => {
|
|
|
118
121
|
};
|
|
119
122
|
|
|
120
123
|
const onTitleChange = ({ target: { value } }) => {
|
|
124
|
+
let error = templateDescErrorHandler(value);
|
|
125
|
+
if (value === '') {
|
|
126
|
+
error = formatMessage(messages.emptyTemplateMessageErrorMessage);
|
|
127
|
+
}
|
|
121
128
|
setTitle(value);
|
|
129
|
+
setTemplateTitleError(error);
|
|
122
130
|
};
|
|
123
131
|
|
|
124
132
|
const onTemplateMediaTypeChange = ({ target: { value } }) => {
|
|
@@ -175,6 +183,11 @@ const CapDeviceContent = (props) => {
|
|
|
175
183
|
value={title}
|
|
176
184
|
size="default"
|
|
177
185
|
isRequired
|
|
186
|
+
errorMessage={templateTitleError && (
|
|
187
|
+
<CapError className="inapp-template-message-error">
|
|
188
|
+
{templateTitleError}
|
|
189
|
+
</CapError>
|
|
190
|
+
)}
|
|
178
191
|
/>
|
|
179
192
|
</CapRow>
|
|
180
193
|
<CapRow className="creatives-inapp-media">
|
|
@@ -24,7 +24,7 @@ const renderComponent = (props) =>
|
|
|
24
24
|
|
|
25
25
|
describe('Test CapDeviceContent', () => {
|
|
26
26
|
it('test case for CapDeviceContent component Android pane', async () => {
|
|
27
|
-
renderComponent(deviceContentProps);
|
|
27
|
+
renderComponent({...deviceContentProps, templateDescErrorHandler: jest.fn()});
|
|
28
28
|
const msgBox = screen.getAllByPlaceholderText(/Please input in-app notification message content/i);
|
|
29
29
|
fireEvent.change(msgBox[0], 'val');
|
|
30
30
|
const copyLink = screen.getByRole('link', {
|
|
@@ -39,11 +39,13 @@ describe('Test CapDeviceContent', () => {
|
|
|
39
39
|
panes: "iOS",
|
|
40
40
|
buttonType: "CTA",
|
|
41
41
|
setTemplateMediaType: jest.fn(),
|
|
42
|
+
setTemplateTitleError: jest.fn(),
|
|
42
43
|
setButtonType: jest.fn(),
|
|
43
44
|
setCtaData: jest.fn(),
|
|
44
45
|
setTemplateMessage: jest.fn(),
|
|
45
46
|
setTemplateMessageError: jest.fn(),
|
|
46
47
|
setTitle: jest.fn(),
|
|
48
|
+
templateDescErrorHandler: jest.fn(),
|
|
47
49
|
templateMediType: "IMAGE",
|
|
48
50
|
addActionLink: true,
|
|
49
51
|
ctaData: [
|
|
@@ -3,7 +3,7 @@ import { FormattedMessage } from 'react-intl';
|
|
|
3
3
|
import messages from './messages';
|
|
4
4
|
|
|
5
5
|
export const DEEP_LINK = 'DEEP_LINK';
|
|
6
|
-
export const
|
|
6
|
+
export const EXTERNAL_URL = 'EXTERNAL_URL';
|
|
7
7
|
export const WEBSITE = 'WEBSITE';
|
|
8
8
|
|
|
9
9
|
export const CTA_OPTIONS = [
|
|
@@ -14,8 +14,8 @@ export const CTA_OPTIONS = [
|
|
|
14
14
|
tooltipLabel: <FormattedMessage {...messages.ctaOptionDisabledTooltipDeepLink} />,
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
|
-
key:
|
|
18
|
-
value:
|
|
17
|
+
key: EXTERNAL_URL,
|
|
18
|
+
value: EXTERNAL_URL,
|
|
19
19
|
label: <FormattedMessage {...messages.ctaLinkTypeExternal} />,
|
|
20
20
|
tooltipLabel: <FormattedMessage {...messages.ctaOptionDisabledTooltipExternalLink} />,
|
|
21
21
|
},
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
import {
|
|
26
26
|
BTN_MAX_LENGTH,
|
|
27
27
|
DEEP_LINK,
|
|
28
|
-
|
|
28
|
+
EXTERNAL_URL,
|
|
29
29
|
CTA_OPTIONS,
|
|
30
30
|
URL_MAX_LENGTH,
|
|
31
31
|
} from "./constants";
|
|
@@ -108,7 +108,7 @@ export const CapInAppCTA = (props) => {
|
|
|
108
108
|
return true;
|
|
109
109
|
} else if (urlType === DEEP_LINK && (ctaDeepLinkValue === "" || urlError)) {
|
|
110
110
|
return true;
|
|
111
|
-
} else if (urlType ===
|
|
111
|
+
} else if (urlType === EXTERNAL_URL && (url === "" || urlError)) {
|
|
112
112
|
return true;
|
|
113
113
|
}
|
|
114
114
|
};
|
|
@@ -223,7 +223,7 @@ export const CapInAppCTA = (props) => {
|
|
|
223
223
|
</>
|
|
224
224
|
|
|
225
225
|
)}
|
|
226
|
-
{urlType ===
|
|
226
|
+
{urlType === EXTERNAL_URL && (
|
|
227
227
|
<>
|
|
228
228
|
<CapHeading type="h4">
|
|
229
229
|
{formatMessage(messages.urlExternalLink)}
|
|
@@ -929,7 +929,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
929
929
|
}
|
|
930
930
|
// Check if template subject present in form data from Loyalty, DVS, Timeline
|
|
931
931
|
if (index === 'template-subject' && type.toLowerCase() === 'embedded' && (currentModule.toLowerCase() === 'loyalty' || currentModule.toLowerCase() === 'dvs' || currentModule.toLowerCase() === 'timeline' || currentModule.toLowerCase() === 'library')) {
|
|
932
|
-
if (
|
|
932
|
+
if (data && data.trim() === '') {
|
|
933
933
|
errorData[index] = true;
|
|
934
934
|
isValid = false;
|
|
935
935
|
} else {
|
|
@@ -14,6 +14,7 @@ import '../PreviewSideBar/_previewsidebar.scss';
|
|
|
14
14
|
import { MOBILE_PUSH } from '../../v2Containers/CreativesContainer/constants';
|
|
15
15
|
import { INAPP } from '../../v2Containers/App/constants';
|
|
16
16
|
import { ANDROID, IOS } from '../../v2Containers/InApp/constants';
|
|
17
|
+
import { getCtaObject } from '../../v2Containers/InApp/utils';
|
|
17
18
|
|
|
18
19
|
class MobilePushPreviewV2 extends React.Component { // eslint-disable-line react/prefer-stateless-function
|
|
19
20
|
constructor(props) {
|
|
@@ -47,35 +48,13 @@ class MobilePushPreviewV2 extends React.Component { // eslint-disable-line react
|
|
|
47
48
|
templateTitle: androidContent?.title,
|
|
48
49
|
templateMsg: androidContent?.message,
|
|
49
50
|
mediaPreview: { inAppImageSrcAndroid: androidContent?.expandableDetails?.image },
|
|
50
|
-
ctaData: androidContent?.expandableDetails?.
|
|
51
|
-
const { index, type, text, url = "", label } = cta;
|
|
52
|
-
const ctaObject = {
|
|
53
|
-
index,
|
|
54
|
-
text,
|
|
55
|
-
url,
|
|
56
|
-
urlType: type,
|
|
57
|
-
isSaved: true,
|
|
58
|
-
label,
|
|
59
|
-
};
|
|
60
|
-
return ctaObject;
|
|
61
|
-
}),
|
|
51
|
+
ctaData: getCtaObject(androidContent?.expandableDetails?.ctas),
|
|
62
52
|
};
|
|
63
53
|
const iosPreviewContent = {
|
|
64
54
|
templateTitle: iosContent?.title,
|
|
65
55
|
templateMsg: iosContent?.message,
|
|
66
56
|
mediaPreview: { inAppImageSrcIos: iosContent?.expandableDetails?.image },
|
|
67
|
-
ctaData: iosContent?.expandableDetails?.
|
|
68
|
-
const { index, type, text, url = "", label } = cta;
|
|
69
|
-
const ctaObject = {
|
|
70
|
-
index,
|
|
71
|
-
text,
|
|
72
|
-
url,
|
|
73
|
-
urlType: type,
|
|
74
|
-
isSaved: true,
|
|
75
|
-
label,
|
|
76
|
-
};
|
|
77
|
-
return ctaObject;
|
|
78
|
-
}),
|
|
57
|
+
ctaData: getCtaObject(iosContent?.expandableDetails?.ctas),
|
|
79
58
|
};
|
|
80
59
|
content = {
|
|
81
60
|
inAppPreviewContent: device === ANDROID.toLowerCase() ? androidPreviewContent : iosPreviewContent,
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
.inapp-content-POPUP-ANDROID {
|
|
41
|
-
margin-top: $
|
|
41
|
+
margin-top: $CAP_SPACE_08;
|
|
42
42
|
margin-right: $CAP_SPACE_08;
|
|
43
43
|
margin-left: $CAP_SPACE_08;
|
|
44
44
|
width: 178px;
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
left: 26%;
|
|
82
82
|
bottom: -21px;
|
|
83
83
|
text-align: center;
|
|
84
|
-
top:
|
|
84
|
+
top: 22px;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
.inapp-image-HEADER-ANDROID {
|
|
@@ -221,7 +221,7 @@
|
|
|
221
221
|
|
|
222
222
|
.inapp-content-FULLSCREEN-ANDROID {
|
|
223
223
|
width: 186px;
|
|
224
|
-
height: 144px;
|
|
224
|
+
max-height: 144px;
|
|
225
225
|
text-align: left;
|
|
226
226
|
margin-top: $CAP_SPACE_04;
|
|
227
227
|
text-overflow: ellipsis;
|
|
@@ -440,7 +440,7 @@
|
|
|
440
440
|
|
|
441
441
|
.inapp-content-FULLSCREEN-iOS {
|
|
442
442
|
width: 186px;
|
|
443
|
-
height: 144px;
|
|
443
|
+
max-height: 144px;
|
|
444
444
|
text-align: left;
|
|
445
445
|
margin-top: $CAP_SPACE_04;
|
|
446
446
|
text-overflow: ellipsis;
|
|
@@ -291,12 +291,14 @@ export class Creatives extends React.Component {
|
|
|
291
291
|
type: channel,
|
|
292
292
|
name: templateData.messageSubject,
|
|
293
293
|
versions: {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
294
|
+
base: {
|
|
295
|
+
content: {
|
|
296
|
+
ANDROID: templateData?.androidContent,
|
|
297
|
+
IOS: templateData?.iosContent,
|
|
298
|
+
},
|
|
297
299
|
},
|
|
298
300
|
},
|
|
299
|
-
definition: {accountId: templateData
|
|
301
|
+
definition: {accountId: templateData?.accountId, mode: mode?.toLowerCase()},
|
|
300
302
|
};
|
|
301
303
|
break;
|
|
302
304
|
}
|
|
@@ -2069,13 +2069,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
2069
2069
|
formData[0].selectedLanguages = [];
|
|
2070
2070
|
let tabKey;
|
|
2071
2071
|
if ((this.props.location.query.isLanguageSupport === undefined || this.props.location.query.isLanguageSupport === 'false')) {
|
|
2072
|
-
|
|
2073
|
-
const baseSubject = data?.base?.subject;
|
|
2074
|
-
if (templateSubject === '') {
|
|
2075
|
-
formData['template-subject'] = '';
|
|
2076
|
-
} else if (!templateSubject && baseSubject) {
|
|
2077
|
-
formData['template-subject'] = baseSubject;
|
|
2078
|
-
}
|
|
2072
|
+
formData['template-subject'] = data.base.subject ? data.base.subject : '';
|
|
2079
2073
|
const baseLanguage = this.props.currentOrgDetails.basic_details.base_language;
|
|
2080
2074
|
let languageIndex = _.findIndex(this.supportedLanguages, {iso_code: baseLanguage});
|
|
2081
2075
|
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { FormattedMessage } from "react-intl";
|
|
3
|
+
import {
|
|
4
|
+
CAP_ORANGE,
|
|
5
|
+
CAP_PURPLE02,
|
|
6
|
+
CAP_PURPLE03,
|
|
7
|
+
CAP_ORANGE01,
|
|
8
|
+
CAP_GREEN01,
|
|
9
|
+
CAP_GREEN02,
|
|
10
|
+
CAP_PALE_GREY,
|
|
11
|
+
FONT_COLOR_05,
|
|
12
|
+
} from '@capillarytech/cap-ui-library/styled/variables';
|
|
3
13
|
import CapHeading from "@capillarytech/cap-ui-library/CapHeading";
|
|
4
14
|
import CapLabel from "@capillarytech/cap-ui-library/CapLabel";
|
|
5
15
|
import { WEBSITE } from "../../v2Components/CapInAppCTA/constants";
|
|
@@ -123,3 +133,28 @@ export const BUTTON_RADIO_OPTIONS = [
|
|
|
123
133
|
),
|
|
124
134
|
},
|
|
125
135
|
];
|
|
136
|
+
|
|
137
|
+
export const INAPP_LAYOUT_DETAILS = {
|
|
138
|
+
POPUP: {
|
|
139
|
+
tagColor: CAP_ORANGE01,
|
|
140
|
+
tagTextColor: CAP_ORANGE,
|
|
141
|
+
text: <FormattedMessage {...messages.popup} />,
|
|
142
|
+
},
|
|
143
|
+
HEADER: {
|
|
144
|
+
tagColor: CAP_PURPLE03,
|
|
145
|
+
tagTextColor: CAP_PURPLE02,
|
|
146
|
+
text: <FormattedMessage {...messages.header} />,
|
|
147
|
+
},
|
|
148
|
+
FOOTER: {
|
|
149
|
+
tagColor: CAP_PALE_GREY,
|
|
150
|
+
tagTextColor: FONT_COLOR_05,
|
|
151
|
+
text: <FormattedMessage {...messages.footer} />,
|
|
152
|
+
},
|
|
153
|
+
FULLSCREEN: {
|
|
154
|
+
tagColor: CAP_GREEN02,
|
|
155
|
+
tagTextColor: CAP_GREEN01,
|
|
156
|
+
text: <FormattedMessage {...messages.layoutFullScreen} />,
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export const DEVICE_SUPPORTED = '1';
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
BIG_PICTURE,
|
|
33
33
|
BIG_TEXT,
|
|
34
34
|
DEEP_LINK,
|
|
35
|
+
DEVICE_SUPPORTED,
|
|
35
36
|
INAPP_BUTTON_TYPES,
|
|
36
37
|
INAPP_MEDIA_TYPES,
|
|
37
38
|
INAPP_MESSAGE_LAYOUT_TYPES,
|
|
@@ -43,6 +44,7 @@ import { INAPP, SMS } from "../CreativesContainer/constants";
|
|
|
43
44
|
import { ALL, TAG, EMBEDDED, DEFAULT, FULL, LIBRARY } from "../Whatsapp/constants";
|
|
44
45
|
import CapDeviceContent from "../../v2Components/CapDeviceContent";
|
|
45
46
|
import { getCdnUrl } from "../../utils/cdnTransformation";
|
|
47
|
+
import { getCtaObject } from "./utils";
|
|
46
48
|
let editContent = {};
|
|
47
49
|
|
|
48
50
|
export const InApp = (props) => {
|
|
@@ -82,6 +84,8 @@ export const InApp = (props) => {
|
|
|
82
84
|
const [templateMessageIos, setTemplateMessageIos] = useState("");
|
|
83
85
|
const [templateMessageErrorAndroid, setTemplateMessageErrorAndroid] = useState(false);
|
|
84
86
|
const [templateMessageErrorIos, setTemplateMessageErrorIos] = useState(false);
|
|
87
|
+
const [templateTitleErrorAndroid, setTemplateTitleErrorAndroid] = useState(false);
|
|
88
|
+
const [templateTitleErrorIos, setTemplateTitleErrorIos] = useState(false);
|
|
85
89
|
const [accountId, setAccountId] = useState("");
|
|
86
90
|
const [accessToken, setAccessToken] = useState("");
|
|
87
91
|
const [accountName, setAccountName] = useState("");
|
|
@@ -116,9 +120,12 @@ export const InApp = (props) => {
|
|
|
116
120
|
configs = {},
|
|
117
121
|
name = "",
|
|
118
122
|
} = accountObj;
|
|
123
|
+
const isAndroidSupported = configs?.android === DEVICE_SUPPORTED;
|
|
124
|
+
// DEVICE_SUPPORTED is '1', which indicates if the particular account is supported, and '0' if the devive is not supported
|
|
119
125
|
//get deep link keys in an array
|
|
120
126
|
const deepLinkKeys = Object.values(JSON.parse(deepLinkObj));
|
|
121
127
|
const keys = deepLinkKeys.map((link) => ({label: link.name, value: link.link, title: link.link }));
|
|
128
|
+
setPanes(isAndroidSupported ? ANDROID : IOS);
|
|
122
129
|
setDeepLink(keys);
|
|
123
130
|
setAccountId(sourceAccountIdentifier);
|
|
124
131
|
setAccessToken(configs.accessToken || "");
|
|
@@ -147,7 +154,7 @@ export const InApp = (props) => {
|
|
|
147
154
|
name = "",
|
|
148
155
|
versions = {},
|
|
149
156
|
createdAt = "",
|
|
150
|
-
} = isFullMode ? editData
|
|
157
|
+
} = isFullMode ? editData?.templateDetails || {} : templateData || {};
|
|
151
158
|
editContent = get(versions, `base.content`, {});
|
|
152
159
|
if (editContent && !isEmpty(editContent)) {
|
|
153
160
|
setEditFlow(true);
|
|
@@ -155,68 +162,60 @@ export const InApp = (props) => {
|
|
|
155
162
|
setTemplateDate(createdAt);
|
|
156
163
|
setTemplateLayoutType(editContent?.ANDROID?.bodyType);
|
|
157
164
|
const androidContent = editContent?.ANDROID;
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if (androidExpandableDetails?.ctas?.length > 0) {
|
|
178
|
-
setCtaDataAndroid(
|
|
179
|
-
androidExpandableDetails?.ctas?.map((cta, index) => {
|
|
180
|
-
const { type, actionText, actionLink = "" } = cta;
|
|
181
|
-
const obj = {
|
|
182
|
-
index,
|
|
183
|
-
text: actionText,
|
|
184
|
-
url: actionLink,
|
|
185
|
-
urlType: type,
|
|
186
|
-
isSaved: true,
|
|
187
|
-
};
|
|
188
|
-
return obj;
|
|
189
|
-
})
|
|
165
|
+
if (!isEmpty(androidContent)) {
|
|
166
|
+
const {
|
|
167
|
+
title: androidTitle = '',
|
|
168
|
+
message: androidMessage = '',
|
|
169
|
+
ctas: androidCta = {},
|
|
170
|
+
expandableDetails: androidExpandableDetails = {},
|
|
171
|
+
} = androidContent || {};
|
|
172
|
+
const {
|
|
173
|
+
style: androidStyle,
|
|
174
|
+
image: androidImage,
|
|
175
|
+
ctas: androidCtas,
|
|
176
|
+
} = androidExpandableDetails || {};
|
|
177
|
+
const androidCtaLength = androidCtas?.length;
|
|
178
|
+
setTitleAndroid(androidTitle);
|
|
179
|
+
setTemplateMessageAndroid(androidMessage);
|
|
180
|
+
setTemplateMediaType(
|
|
181
|
+
androidStyle === BIG_TEXT
|
|
182
|
+
? INAPP_MEDIA_TYPES.TEXT
|
|
183
|
+
: INAPP_MEDIA_TYPES.IMAGE
|
|
190
184
|
);
|
|
185
|
+
setInAppImageSrcAndroid(androidImage);
|
|
186
|
+
setDeepLinkValueAndroid(androidCta[0]?.actionLink);
|
|
187
|
+
setAddActionLinkAndroid(!isEmpty(androidCta) && true);
|
|
188
|
+
setButtonTypeAndroid(
|
|
189
|
+
androidCtaLength ? INAPP_BUTTON_TYPES.CTA : INAPP_BUTTON_TYPES.NONE
|
|
190
|
+
);
|
|
191
|
+
if (androidCtaLength > 0) {
|
|
192
|
+
setCtaDataAndroid(getCtaObject(androidCtas));
|
|
193
|
+
}
|
|
191
194
|
}
|
|
192
195
|
const iosContent = editContent?.IOS;
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
};
|
|
217
|
-
return obj;
|
|
218
|
-
})
|
|
219
|
-
);
|
|
196
|
+
if (!isEmpty(iosContent)) {
|
|
197
|
+
const {
|
|
198
|
+
title: iosTitle = '',
|
|
199
|
+
message: iosMessage = '',
|
|
200
|
+
ctas: iosCta = {},
|
|
201
|
+
expandableDetails: iosExpandableDetails = {},
|
|
202
|
+
} = iosContent || {};
|
|
203
|
+
const {
|
|
204
|
+
style: iosStyle,
|
|
205
|
+
image: iosImage,
|
|
206
|
+
ctas: iosCtas,
|
|
207
|
+
} = iosExpandableDetails || {};
|
|
208
|
+
const iosCtaLength = iosCtas?.length;
|
|
209
|
+
setTitleIos(iosTitle);
|
|
210
|
+
setTemplateMessageIos(iosMessage);
|
|
211
|
+
setTemplateMediaType(iosStyle === BIG_TEXT ? INAPP_MEDIA_TYPES.TEXT : INAPP_MEDIA_TYPES.IMAGE);
|
|
212
|
+
setInAppImageSrcIos(iosImage);
|
|
213
|
+
setButtonTypeIos(iosCtaLength ? INAPP_BUTTON_TYPES.CTA : INAPP_BUTTON_TYPES.NONE);
|
|
214
|
+
setAddActionLinkIos(!isEmpty(iosCta) && true);
|
|
215
|
+
setDeepLinkValueIos(iosCta[0]?.actionLink);
|
|
216
|
+
if (iosCtaLength > 0) {
|
|
217
|
+
setCtaDataIos(getCtaObject(iosCtas));
|
|
218
|
+
}
|
|
220
219
|
}
|
|
221
220
|
}
|
|
222
221
|
}, [editData.templateDetails || templateData]);
|
|
@@ -359,6 +358,8 @@ export const InApp = (props) => {
|
|
|
359
358
|
templateMessage={templateMessageAndroid}
|
|
360
359
|
setTemplateMessage={setTemplateMessageAndroid}
|
|
361
360
|
setTemplateMessageError={setTemplateMessageErrorAndroid}
|
|
361
|
+
templateTitleError={templateTitleErrorAndroid}
|
|
362
|
+
setTemplateTitleError={setTemplateTitleErrorAndroid}
|
|
362
363
|
addActionLink={addActionLinkAndroid}
|
|
363
364
|
setAddActionLink={setAddActionLinkAndroid}
|
|
364
365
|
deepLink={deepLink}
|
|
@@ -373,6 +374,8 @@ export const InApp = (props) => {
|
|
|
373
374
|
),
|
|
374
375
|
tab: <FormattedMessage {...messages.Android} />,
|
|
375
376
|
key: ANDROID,
|
|
377
|
+
isSupported: get(accountData, 'selectedWeChatAccount.configs.android') === DEVICE_SUPPORTED,
|
|
378
|
+
// DEVICE_SUPPORTED is '1', which indicates if the particular account is supported, and '0' if the devive is not supported
|
|
376
379
|
},
|
|
377
380
|
{
|
|
378
381
|
content: (
|
|
@@ -402,6 +405,8 @@ export const InApp = (props) => {
|
|
|
402
405
|
templateMessage={templateMessageIos}
|
|
403
406
|
setTemplateMessage={setTemplateMessageIos}
|
|
404
407
|
setTemplateMessageError={setTemplateMessageErrorIos}
|
|
408
|
+
templateTitleError={templateTitleErrorIos}
|
|
409
|
+
setTemplateTitleError={setTemplateTitleErrorIos}
|
|
405
410
|
addActionLink={addActionLinkIos}
|
|
406
411
|
setAddActionLink={setAddActionLinkIos}
|
|
407
412
|
deepLink={deepLink}
|
|
@@ -411,10 +416,12 @@ export const InApp = (props) => {
|
|
|
411
416
|
tags={tags}
|
|
412
417
|
onTagSelect={onTagSelect}
|
|
413
418
|
handleOnTagsContextChange={handleOnTagsContextChange}
|
|
419
|
+
templateDescErrorHandler={templateDescErrorHandler}
|
|
414
420
|
/>
|
|
415
421
|
),
|
|
416
422
|
tab: <FormattedMessage {...messages.Ios} />,
|
|
417
423
|
key: IOS,
|
|
424
|
+
isSupported: get(accountData, 'selectedWeChatAccount.configs.ios') === DEVICE_SUPPORTED,
|
|
418
425
|
},
|
|
419
426
|
];
|
|
420
427
|
|
|
@@ -499,24 +506,35 @@ export const InApp = (props) => {
|
|
|
499
506
|
);
|
|
500
507
|
};
|
|
501
508
|
|
|
502
|
-
const isDisableDone = () => {
|
|
509
|
+
const isDisableDone = (device) => {
|
|
510
|
+
const isIosDevice = device === IOS;
|
|
511
|
+
const isAndroidDevice = device === ANDROID;
|
|
503
512
|
//if template name is not entered
|
|
504
|
-
if (templateName.trim() === '') {
|
|
513
|
+
if (isFullMode && templateName.trim() === '') {
|
|
505
514
|
return true;
|
|
506
515
|
}
|
|
507
516
|
//if template message is not entered
|
|
508
517
|
//for android
|
|
509
|
-
if (
|
|
518
|
+
if (isAndroidDevice && (templateMessageAndroid.trim() === '' || templateMessageErrorAndroid)) {
|
|
510
519
|
return true;
|
|
511
520
|
}
|
|
512
|
-
if (
|
|
521
|
+
if (isIosDevice && (templateMessageIos.trim() === '' || templateMessageErrorIos)) {
|
|
522
|
+
return true;
|
|
523
|
+
}//for ios
|
|
524
|
+
|
|
525
|
+
//if template title is not entered
|
|
526
|
+
//for android
|
|
527
|
+
if (isAndroidDevice && (titleAndroid.trim() === '' || templateTitleErrorAndroid)) {
|
|
528
|
+
return true;
|
|
529
|
+
}
|
|
530
|
+
if (isIosDevice && (titleIos.trim() === '' || templateTitleErrorIos)) {
|
|
513
531
|
return true;
|
|
514
532
|
}//for ios
|
|
515
533
|
//if media type is image and the mediaType file is not uploaded
|
|
516
|
-
if (
|
|
534
|
+
if (isAndroidDevice && !(templateMediaType === INAPP_MEDIA_TYPES.TEXT) && inAppImageSrcAndroid === '') {
|
|
517
535
|
return true;
|
|
518
536
|
}
|
|
519
|
-
if (
|
|
537
|
+
if (isIosDevice && !(templateMediaType === INAPP_MEDIA_TYPES.TEXT) && inAppImageSrcIos === '') {
|
|
520
538
|
return true;
|
|
521
539
|
}
|
|
522
540
|
//cta
|
|
@@ -581,7 +599,7 @@ export const InApp = (props) => {
|
|
|
581
599
|
versions: {
|
|
582
600
|
base: {
|
|
583
601
|
content: {
|
|
584
|
-
ANDROID: {
|
|
602
|
+
ANDROID: !isDisableDone(ANDROID) ? {
|
|
585
603
|
...commonDevicePayload,
|
|
586
604
|
title: titleAndroid,
|
|
587
605
|
message: templateMessageAndroid,
|
|
@@ -595,9 +613,11 @@ export const InApp = (props) => {
|
|
|
595
613
|
}),
|
|
596
614
|
},
|
|
597
615
|
custom: [],
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
616
|
+
...(deepLinkValueAndroid && {
|
|
617
|
+
ctas: [{type: DEEP_LINK, actionLink: deepLinkValueAndroid}],
|
|
618
|
+
}),
|
|
619
|
+
} : {},
|
|
620
|
+
IOS: !isDisableDone(IOS) ? {
|
|
601
621
|
...commonDevicePayload,
|
|
602
622
|
title: titleIos,
|
|
603
623
|
message: templateMessageIos,
|
|
@@ -611,8 +631,10 @@ export const InApp = (props) => {
|
|
|
611
631
|
}),
|
|
612
632
|
},
|
|
613
633
|
custom: [],
|
|
614
|
-
|
|
615
|
-
|
|
634
|
+
...(deepLinkValueIos && {
|
|
635
|
+
ctas: [{type: DEEP_LINK, actionLink: deepLinkValueIos}],
|
|
636
|
+
}),
|
|
637
|
+
} : {},
|
|
616
638
|
},
|
|
617
639
|
},
|
|
618
640
|
},
|
|
@@ -696,7 +718,7 @@ export const InApp = (props) => {
|
|
|
696
718
|
{isFullMode && createModeContent}
|
|
697
719
|
{/* device tab */}
|
|
698
720
|
<CapTab
|
|
699
|
-
panes={PANES}
|
|
721
|
+
panes={PANES.filter((devicePane) => devicePane?.isSupported === true)}
|
|
700
722
|
onChange={(value) => {
|
|
701
723
|
setPanes(value);
|
|
702
724
|
}}
|
|
@@ -710,12 +732,12 @@ export const InApp = (props) => {
|
|
|
710
732
|
{getPreviewSection()}
|
|
711
733
|
</CapColumn>
|
|
712
734
|
</CapRow>
|
|
713
|
-
<div className=
|
|
735
|
+
<div className={`inapp-footer ${!isFullMode && `inapp-footer-lib`}`}>
|
|
714
736
|
{
|
|
715
737
|
<>
|
|
716
738
|
<CapButton
|
|
717
739
|
onClick={onDoneCallback()}
|
|
718
|
-
disabled={isDisableDone()}
|
|
740
|
+
disabled={isDisableDone(panes)}
|
|
719
741
|
className="inapp-create-btn"
|
|
720
742
|
>
|
|
721
743
|
{isEditFlow ? (
|
|
@@ -725,7 +747,11 @@ export const InApp = (props) => {
|
|
|
725
747
|
<FormattedMessage {...globalMessages.done} />
|
|
726
748
|
)
|
|
727
749
|
) : (
|
|
728
|
-
|
|
750
|
+
isFullMode ? (
|
|
751
|
+
<FormattedMessage {...messages.create} />
|
|
752
|
+
) : (
|
|
753
|
+
<FormattedMessage {...globalMessages.done} />
|
|
754
|
+
)
|
|
729
755
|
)}
|
|
730
756
|
</CapButton>
|
|
731
757
|
</>
|
|
@@ -37,7 +37,6 @@
|
|
|
37
37
|
.inapp-footer {
|
|
38
38
|
background-color: $CAP_WHITE;
|
|
39
39
|
bottom: 0;
|
|
40
|
-
// position: fixed;
|
|
41
40
|
width: 100%;
|
|
42
41
|
padding: $CAP_SPACE_20 0;
|
|
43
42
|
z-index: 1;
|
|
@@ -45,4 +44,7 @@
|
|
|
45
44
|
.ant-btn {
|
|
46
45
|
margin-right: $CAP_SPACE_16;
|
|
47
46
|
}
|
|
47
|
+
}
|
|
48
|
+
.inapp-footer-lib {
|
|
49
|
+
position: fixed;
|
|
48
50
|
}
|
|
@@ -96,4 +96,19 @@ export default defineMessages({
|
|
|
96
96
|
defaultMessage:
|
|
97
97
|
"Create a button that lets customers take an action",
|
|
98
98
|
},
|
|
99
|
+
popup: {
|
|
100
|
+
id: `${prefix}.popup`,
|
|
101
|
+
defaultMessage:
|
|
102
|
+
"Pop-up",
|
|
103
|
+
},
|
|
104
|
+
header: {
|
|
105
|
+
id: `${prefix}.header`,
|
|
106
|
+
defaultMessage:
|
|
107
|
+
"Header",
|
|
108
|
+
},
|
|
109
|
+
footer: {
|
|
110
|
+
id: `${prefix}.footer`,
|
|
111
|
+
defaultMessage:
|
|
112
|
+
"Footer",
|
|
113
|
+
},
|
|
99
114
|
});
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
location1,
|
|
15
15
|
} from './mockData';
|
|
16
16
|
import { metaEntities, getDefaultTags, injectedTags } from '../../Zalo/tests/mockData';
|
|
17
|
+
import { getCtaObject } from '../utils';
|
|
17
18
|
|
|
18
19
|
const mockActions = {
|
|
19
20
|
getTemplateInfoById: jest.fn(),
|
|
@@ -140,3 +141,23 @@ describe('Test activity inApp container', () => {
|
|
|
140
141
|
expect(copyLink).toBeInTheDocument();
|
|
141
142
|
});
|
|
142
143
|
});
|
|
144
|
+
describe('test for getCtaObject function', () => {
|
|
145
|
+
it('getCtaObject function', () => {
|
|
146
|
+
const response = getCtaObject([{ type: "DEEP_LINK", actionText: "btn", actionLink: "url" }]);
|
|
147
|
+
expect(response).toEqual([{
|
|
148
|
+
index: 0,
|
|
149
|
+
text: "btn",
|
|
150
|
+
url: "url",
|
|
151
|
+
urlType: "DEEP_LINK",
|
|
152
|
+
isSaved: true,
|
|
153
|
+
}]);
|
|
154
|
+
const responseDefault = getCtaObject([{}]);
|
|
155
|
+
expect(responseDefault).toEqual([{
|
|
156
|
+
index: 0,
|
|
157
|
+
text: "",
|
|
158
|
+
url: "",
|
|
159
|
+
urlType: "",
|
|
160
|
+
isSaved: true,
|
|
161
|
+
}]);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
@@ -171,7 +171,7 @@ export const accountData = {
|
|
|
171
171
|
buttons: [
|
|
172
172
|
{
|
|
173
173
|
index: 0,
|
|
174
|
-
type: "
|
|
174
|
+
type: "EXTERNAL_URL",
|
|
175
175
|
text: "test",
|
|
176
176
|
url: "https://nightly.intouch.capillarytech.com/creatives/ui/v2",
|
|
177
177
|
label: "Test_1",
|
|
@@ -196,7 +196,7 @@ export const accountData = {
|
|
|
196
196
|
buttons: [
|
|
197
197
|
{
|
|
198
198
|
index: 0,
|
|
199
|
-
type: "
|
|
199
|
+
type: "EXTERNAL_URL",
|
|
200
200
|
text: "test",
|
|
201
201
|
url: "https://nightly.intouch.capillarytech.com/creatives/ui/v2",
|
|
202
202
|
label: "Test_1",
|
|
@@ -247,7 +247,7 @@ export const accountData = {
|
|
|
247
247
|
buttons: [
|
|
248
248
|
{
|
|
249
249
|
index: 0,
|
|
250
|
-
type: "
|
|
250
|
+
type: "EXTERNAL_URL",
|
|
251
251
|
text: "test",
|
|
252
252
|
url: "https://nightly.intouch.capillarytech.com/creatives/ui/v2",
|
|
253
253
|
label: "Test_1",
|
|
@@ -276,7 +276,7 @@ export const accountData = {
|
|
|
276
276
|
buttons: [
|
|
277
277
|
{
|
|
278
278
|
index: 0,
|
|
279
|
-
type: "
|
|
279
|
+
type: "EXTERNAL_URL",
|
|
280
280
|
text: "testios",
|
|
281
281
|
url: "https://nightly.intouch.capillarytech.com/creatives/ui/v2",
|
|
282
282
|
label: "",
|
|
@@ -671,7 +671,7 @@ export const editData = {
|
|
|
671
671
|
buttons: [
|
|
672
672
|
{
|
|
673
673
|
index: 0,
|
|
674
|
-
type: "
|
|
674
|
+
type: "EXTERNAL_URL",
|
|
675
675
|
text: "test",
|
|
676
676
|
url: "https://nightly.intouch.capillarytech.com/creatives/ui/v2",
|
|
677
677
|
label: "Test_1",
|
|
@@ -697,7 +697,7 @@ export const editData = {
|
|
|
697
697
|
buttons: [
|
|
698
698
|
{
|
|
699
699
|
index: 0,
|
|
700
|
-
type: "
|
|
700
|
+
type: "EXTERNAL_URL",
|
|
701
701
|
text: "test",
|
|
702
702
|
url: "https://nightly.intouch.capillarytech.com/creatives/ui/v2",
|
|
703
703
|
label: "Test_1",
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const getCtaObject = (ctaData) =>
|
|
2
|
+
ctaData?.map((cta, index) => {
|
|
3
|
+
const { type = "", actionText = "", actionLink = "" } = cta;
|
|
4
|
+
const obj = {
|
|
5
|
+
index,
|
|
6
|
+
text: actionText,
|
|
7
|
+
url: actionLink,
|
|
8
|
+
urlType: type,
|
|
9
|
+
isSaved: true,
|
|
10
|
+
};
|
|
11
|
+
return obj;
|
|
12
|
+
});
|
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
CapInfoNote,
|
|
50
50
|
CapImage,
|
|
51
51
|
CapStatus,
|
|
52
|
+
CapColoredTag,
|
|
52
53
|
} from '@capillarytech/cap-ui-library';
|
|
53
54
|
import { makeSelectTemplates, makeSelectTemplatesResponse } from './selectors';
|
|
54
55
|
import { makeSelectCreate as makeSelectCreateSms} from '../Sms/Create/selectors';
|
|
@@ -108,6 +109,7 @@ import {
|
|
|
108
109
|
HOST_TWILIO,
|
|
109
110
|
HOST_GUPSHUP,
|
|
110
111
|
} from '../Whatsapp/constants';
|
|
112
|
+
import { INAPP_LAYOUT_DETAILS } from '../InApp/constants';
|
|
111
113
|
import { ZALO_STATUS_OPTIONS, ZALO_STATUSES } from '../Zalo/constants';
|
|
112
114
|
import { getWhatsappContent, getWhatsappStatus, getWhatsappCategory, getWhatsappCta, getWhatsappQuickReply } from '../Whatsapp/utils';
|
|
113
115
|
import { getRCSContent } from '../Rcs/utils';
|
|
@@ -823,7 +825,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
823
825
|
queryParams.wecrmToken = this.props.Templates.selectedWeChatAccount.configs.wecrm_token;
|
|
824
826
|
queryParams.originalId = this.props.Templates.selectedWeChatAccount.sourceAccountIdentifier;
|
|
825
827
|
}
|
|
826
|
-
if (([MOBILE_PUSH_LOWERCASE, INAPP_LOWERCASE.includes
|
|
828
|
+
if (([MOBILE_PUSH_LOWERCASE, INAPP_LOWERCASE].includes(this.state.channel.toLowerCase())) && !isEmpty(this.props.Templates.selectedWeChatAccount)) {
|
|
827
829
|
queryParams.accountId = this.props.Templates.selectedWeChatAccount.id;
|
|
828
830
|
if (this.state.mode) {
|
|
829
831
|
queryParams.mode = this.state.mode.toLowerCase();
|
|
@@ -1004,22 +1006,73 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
1004
1006
|
);
|
|
1005
1007
|
|
|
1006
1008
|
const cardDataList = filteredTemplates && filteredTemplates.length ? map(filteredTemplates, (template) => {
|
|
1007
|
-
const
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1009
|
+
const androidBodyType = get(template, 'versions.base.content.ANDROID.bodyType');
|
|
1010
|
+
const iosBodyType = get(template, 'versions.base.content.IOS.bodyType');
|
|
1011
|
+
const inappBodyType = androidBodyType || iosBodyType;
|
|
1012
|
+
const templateData = {
|
|
1013
|
+
key: `${currentChannel}-card-${template?.name}`,
|
|
1014
|
+
title: (
|
|
1015
|
+
<span title={template?.name}>
|
|
1016
|
+
{template?.name}
|
|
1017
|
+
{currentChannel === INAPP && (
|
|
1018
|
+
<CapRow>
|
|
1019
|
+
<CapColoredTag
|
|
1020
|
+
tagColor={INAPP_LAYOUT_DETAILS[inappBodyType]?.tagColor}
|
|
1021
|
+
tagTextColor={
|
|
1022
|
+
INAPP_LAYOUT_DETAILS[inappBodyType]?.tagTextColor
|
|
1023
|
+
}
|
|
1024
|
+
tagHeight="1.25rem"
|
|
1025
|
+
tagFontSize="12px"
|
|
1026
|
+
>
|
|
1027
|
+
{INAPP_LAYOUT_DETAILS[inappBodyType]?.text}
|
|
1028
|
+
</CapColoredTag>
|
|
1029
|
+
</CapRow>
|
|
1030
|
+
)}
|
|
1031
|
+
</span>
|
|
1032
|
+
),
|
|
1033
|
+
extra: [
|
|
1034
|
+
<CapTooltip
|
|
1035
|
+
title={
|
|
1036
|
+
this.state.channel.toLowerCase() === ZALO_LOWERCASE ? (
|
|
1037
|
+
<div className="zalo-view-tooltip">
|
|
1038
|
+
{this.props.intl.formatMessage(messages.zaloPreview)}
|
|
1039
|
+
</div>
|
|
1040
|
+
) : (
|
|
1041
|
+
""
|
|
1042
|
+
)
|
|
1043
|
+
}
|
|
1044
|
+
>
|
|
1045
|
+
<CapIcon
|
|
1046
|
+
className={`view-${channelLowerCase}`}
|
|
1047
|
+
style={{ marginRight: "16px" }}
|
|
1048
|
+
type="eye"
|
|
1049
|
+
onClick={() => {
|
|
1050
|
+
if (!this.props.isFullMode || this.props.isDltFromRcs) {
|
|
1051
|
+
this.props.handlePeviewTemplate(template);
|
|
1052
|
+
} else {
|
|
1053
|
+
handlers.handlePreviewClick(template);
|
|
1054
|
+
}
|
|
1055
|
+
}}
|
|
1056
|
+
/>
|
|
1057
|
+
</CapTooltip>,
|
|
1058
|
+
],
|
|
1059
|
+
hoverOption: (
|
|
1060
|
+
<CapButton
|
|
1061
|
+
className={
|
|
1062
|
+
this.props.isFullMode
|
|
1063
|
+
? `edit-${channelLowerCase}`
|
|
1064
|
+
: `select-${channelLowerCase}`
|
|
1065
|
+
}
|
|
1066
|
+
onClick={(e) =>
|
|
1067
|
+
handlers.handleEditClick(e, template, undefined, undefined, {
|
|
1068
|
+
account: this.state.selectedAccount,
|
|
1069
|
+
})
|
|
1070
|
+
}
|
|
1071
|
+
>
|
|
1072
|
+
{hoverButtonText}
|
|
1073
|
+
</CapButton>
|
|
1074
|
+
),
|
|
1075
|
+
};
|
|
1023
1076
|
const {
|
|
1024
1077
|
versions: {
|
|
1025
1078
|
base: {
|