@capillarytech/creatives-library 8.0.125 → 8.0.127-alpha.0
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/config/app.js +6 -0
- package/containers/App/constants.js +1 -0
- package/index.html +3 -1
- package/initialReducer.js +2 -0
- package/package.json +1 -1
- package/services/api.js +94 -1
- package/services/tests/api.test.js +191 -0
- package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +8 -3
- package/tests/integration/TemplateCreation/api-response.js +5 -0
- package/tests/integration/TemplateCreation/msw-handler.js +42 -63
- package/utils/common.js +7 -0
- package/utils/commonUtils.js +2 -6
- package/utils/createPayload.js +272 -0
- package/utils/tests/createPayload.test.js +761 -0
- package/v2Components/CapImageUpload/index.js +59 -46
- package/v2Components/CapInAppCTA/index.js +1 -0
- package/v2Components/CapMpushCTA/constants.js +25 -0
- package/v2Components/CapMpushCTA/index.js +332 -0
- package/v2Components/CapMpushCTA/index.scss +95 -0
- package/v2Components/CapMpushCTA/messages.js +89 -0
- package/v2Components/CapTagList/index.js +177 -120
- package/v2Components/CapVideoUpload/constants.js +3 -0
- package/v2Components/CapVideoUpload/index.js +167 -110
- package/v2Components/CapVideoUpload/messages.js +16 -0
- package/v2Components/Carousel/index.js +15 -13
- package/v2Components/CustomerSearchSection/_customerSearch.scss +309 -0
- package/v2Components/CustomerSearchSection/constants.js +5 -0
- package/v2Components/CustomerSearchSection/index.js +367 -0
- package/v2Components/CustomerSearchSection/messages.js +20 -0
- package/v2Components/CustomerSearchSection/tests/utils.test.js +334 -0
- package/v2Components/CustomerSearchSection/utils.js +49 -0
- package/v2Components/ErrorInfoNote/style.scss +1 -0
- package/v2Components/MobilePushPreviewV2/index.js +37 -5
- package/v2Components/TemplatePreview/_templatePreview.scss +114 -72
- package/v2Components/TemplatePreview/assets/images/Android _ With date and time.svg +29 -0
- package/v2Components/TemplatePreview/assets/images/android.svg +9 -0
- package/v2Components/TemplatePreview/assets/images/iOS _ With date and time.svg +26 -0
- package/v2Components/TemplatePreview/assets/images/ios.svg +9 -0
- package/v2Components/TemplatePreview/index.js +178 -50
- package/v2Components/TemplatePreview/messages.js +4 -0
- package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +169 -0
- package/v2Components/TestAndPreviewSlidebox/LeftPanelContent.js +95 -0
- package/v2Components/TestAndPreviewSlidebox/PreviewSection.js +69 -0
- package/v2Components/TestAndPreviewSlidebox/SendTestMessage.js +68 -0
- package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +543 -0
- package/v2Components/TestAndPreviewSlidebox/actions.js +67 -0
- package/v2Components/TestAndPreviewSlidebox/constants.js +67 -0
- package/v2Components/TestAndPreviewSlidebox/index.js +592 -0
- package/v2Components/TestAndPreviewSlidebox/messages.js +147 -0
- package/v2Components/TestAndPreviewSlidebox/reducer.js +233 -0
- package/v2Components/TestAndPreviewSlidebox/sagas.js +258 -0
- package/v2Components/TestAndPreviewSlidebox/selectors.js +142 -0
- package/v2Components/TestAndPreviewSlidebox/tests/CustomValuesEditor.test.js +425 -0
- package/v2Components/TestAndPreviewSlidebox/tests/LeftPanelContent.test.js +400 -0
- package/v2Components/TestAndPreviewSlidebox/tests/SendTestMessage.test.js +448 -0
- package/v2Components/TestAndPreviewSlidebox/tests/actions.test.js +80 -0
- package/v2Components/TestAndPreviewSlidebox/tests/reducer.test.js +367 -0
- package/v2Components/TestAndPreviewSlidebox/tests/saga.rtl.test.js +192 -0
- package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +652 -0
- package/v2Components/TestAndPreviewSlidebox/tests/selector.test.js +182 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +22 -10
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +23 -2
- package/v2Containers/CreativesContainer/index.js +216 -136
- package/v2Containers/CreativesContainer/messages.js +4 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +21 -0
- package/v2Containers/Email/index.js +27 -2
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +10 -0
- package/v2Containers/EmailWrapper/index.js +6 -0
- package/v2Containers/InApp/constants.js +1 -0
- package/v2Containers/InApp/index.js +13 -13
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -0
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -0
- package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -0
- package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -0
- package/v2Containers/MobilePush/Create/index.js +1 -0
- package/v2Containers/MobilePush/commonMethods.js +7 -14
- package/v2Containers/MobilePushNew/actions.js +116 -0
- package/v2Containers/MobilePushNew/components/CtaButtons.js +170 -0
- package/v2Containers/MobilePushNew/components/MediaUploaders.js +754 -0
- package/v2Containers/MobilePushNew/components/PlatformContentFields.js +279 -0
- package/v2Containers/MobilePushNew/components/index.js +5 -0
- package/v2Containers/MobilePushNew/components/tests/CtaButtons.test.js +779 -0
- package/v2Containers/MobilePushNew/components/tests/MediaUploaders.test.js +2114 -0
- package/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +343 -0
- package/v2Containers/MobilePushNew/constants.js +115 -0
- package/v2Containers/MobilePushNew/hooks/tests/usePlatformSync.test.js +1299 -0
- package/v2Containers/MobilePushNew/hooks/tests/useUpload.test.js +1223 -0
- package/v2Containers/MobilePushNew/hooks/usePlatformSync.js +246 -0
- package/v2Containers/MobilePushNew/hooks/useUpload.js +726 -0
- package/v2Containers/MobilePushNew/index.js +2280 -0
- package/v2Containers/MobilePushNew/index.scss +308 -0
- package/v2Containers/MobilePushNew/messages.js +226 -0
- package/v2Containers/MobilePushNew/reducer.js +160 -0
- package/v2Containers/MobilePushNew/sagas.js +198 -0
- package/v2Containers/MobilePushNew/selectors.js +55 -0
- package/v2Containers/MobilePushNew/tests/reducer.test.js +741 -0
- package/v2Containers/MobilePushNew/tests/sagas.test.js +863 -0
- package/v2Containers/MobilePushNew/tests/selectors.test.js +425 -0
- package/v2Containers/MobilePushNew/tests/utils.test.js +322 -0
- package/v2Containers/MobilePushNew/utils.js +33 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +23 -5
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -0
- package/v2Containers/TagList/index.js +56 -10
- package/v2Containers/Templates/_templates.scss +101 -1
- package/v2Containers/Templates/index.js +147 -35
- package/v2Containers/Templates/messages.js +8 -0
- package/v2Containers/Templates/sagas.js +2 -0
- package/v2Containers/Whatsapp/constants.js +1 -0
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +35 -0
- package/v2Containers/Email/tests/index.test.js +0 -35
|
@@ -22,7 +22,9 @@ import { isEmpty, get } from 'lodash';
|
|
|
22
22
|
import './index.scss';
|
|
23
23
|
import Gallery from '../../v2Containers/Assets/Gallery';
|
|
24
24
|
import { MAX_SUPPORTED_IMAGE_SIZE } from './constants';
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
FACEBOOK, INAPP, RCS, WHATSAPP, VIBER,
|
|
27
|
+
} from "../../v2Containers/CreativesContainer/constants";
|
|
26
28
|
|
|
27
29
|
import messages from './messages';
|
|
28
30
|
function CapImageUpload(props) {
|
|
@@ -45,19 +47,25 @@ function CapImageUpload(props) {
|
|
|
45
47
|
channel,
|
|
46
48
|
channelSpecificStyle,
|
|
47
49
|
showReUploadButton = true,
|
|
50
|
+
disableAutoRestore = false, // New prop to disable automatic restoration
|
|
48
51
|
} = props;
|
|
49
52
|
const {
|
|
50
53
|
formatMessage,
|
|
51
|
-
|
|
54
|
+
} = intl || {};
|
|
52
55
|
|
|
53
56
|
|
|
54
57
|
useEffect(() => {
|
|
58
|
+
// Skip automatic restoration if disableAutoRestore is true
|
|
59
|
+
if (disableAutoRestore) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
55
63
|
const imageDataObj = imageData[`uploadedAssetData${index}`];
|
|
56
64
|
if (!isEmpty(imageDataObj)) {
|
|
57
|
-
const { secure_file_path = ''
|
|
58
|
-
updateImageSrc(secure_file_path
|
|
65
|
+
const { secure_file_path = '' } = get(imageDataObj, 'metaInfo', {});
|
|
66
|
+
updateImageSrc(secure_file_path);
|
|
59
67
|
}
|
|
60
|
-
}, [imageData[`uploadedAssetData${index}`]]);
|
|
68
|
+
}, [imageData[`uploadedAssetData${index}`], disableAutoRestore]);
|
|
61
69
|
|
|
62
70
|
const [isImageError, updateImageErrorMessage] = useState(false);
|
|
63
71
|
const [isDrawerRequired, updateDrawerRequirement] = useState(false);
|
|
@@ -65,7 +73,7 @@ function CapImageUpload(props) {
|
|
|
65
73
|
const {CapHeadingSpan} = CapHeading;
|
|
66
74
|
const ImageComponent = useCallback(
|
|
67
75
|
() => (
|
|
68
|
-
|
|
76
|
+
<>
|
|
69
77
|
{
|
|
70
78
|
!isEmpty(imageSrc) ? (
|
|
71
79
|
<div className={`image-container upload ${props.ifError ? 'error' : ''}`}>
|
|
@@ -74,10 +82,10 @@ function CapImageUpload(props) {
|
|
|
74
82
|
)}
|
|
75
83
|
</div>
|
|
76
84
|
) : null}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
</>
|
|
86
|
+
),
|
|
87
|
+
[imageSrc],
|
|
88
|
+
);
|
|
81
89
|
|
|
82
90
|
const WithLabel = LabelHOC(ImageComponent);
|
|
83
91
|
|
|
@@ -147,10 +155,13 @@ function CapImageUpload(props) {
|
|
|
147
155
|
const onReUpload = useCallback(() => {
|
|
148
156
|
updateImageSrc('');
|
|
149
157
|
updateOnReUpload();
|
|
158
|
+
// Don't automatically trigger file dialog - let user choose between computer and gallery
|
|
150
159
|
}, []);
|
|
151
160
|
|
|
152
161
|
const onGalleryImageSelect = useCallback((imageTemplate) => {
|
|
153
|
-
const {
|
|
162
|
+
const {
|
|
163
|
+
secure_file_path: image, width, height, file_size: size,
|
|
164
|
+
} = get(imageTemplate, 'metaInfo', {});
|
|
154
165
|
updateDrawerRequirement(false);
|
|
155
166
|
if (!allowedExtensionsRegex.test(image) || height > imgHeight || width > imgWidth || size > imgSize ) {
|
|
156
167
|
updateImageErrorMessage(formatMessage(messages.imageErrorDesc));
|
|
@@ -176,7 +187,7 @@ function CapImageUpload(props) {
|
|
|
176
187
|
isFullMode={isFullMode}
|
|
177
188
|
isLineAsset
|
|
178
189
|
onGalleryImageSelect={onGalleryImageSelect}
|
|
179
|
-
|
|
190
|
+
/>
|
|
180
191
|
</>
|
|
181
192
|
);
|
|
182
193
|
}, []);
|
|
@@ -194,34 +205,35 @@ function CapImageUpload(props) {
|
|
|
194
205
|
|
|
195
206
|
const getImageSection = useCallback(() => {
|
|
196
207
|
if (imageSrc === "") {
|
|
197
|
-
return (
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
<
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
<
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
<
|
|
211
|
-
|
|
212
|
-
{channel !== WHATSAPP && (
|
|
213
|
-
<CapButton
|
|
214
|
-
className="dragger-button gallery-select"
|
|
215
|
-
type="secondary"
|
|
216
|
-
onClick={onGalleryClick}
|
|
217
|
-
>
|
|
218
|
-
<FormattedMessage {...messages.uploadGallery} />
|
|
208
|
+
return (
|
|
209
|
+
<>
|
|
210
|
+
<CapUploader.CapDragger
|
|
211
|
+
customRequest={capUploaderCustomRequest}
|
|
212
|
+
className="form-builder-dragger grey-background"
|
|
213
|
+
showUploadList={!isImageError}
|
|
214
|
+
>
|
|
215
|
+
<CapHeading className="dragger-title" type="h7">
|
|
216
|
+
<FormattedMessage {...messages.dragAndDrop} />
|
|
217
|
+
</CapHeading>
|
|
218
|
+
<CapHeading className="dragger-or" type="label6">
|
|
219
|
+
<FormattedMessage {...messages.or} />
|
|
220
|
+
</CapHeading>
|
|
221
|
+
<CapButton className="dragger-button upload-image" type="secondary">
|
|
222
|
+
<FormattedMessage {...messages.uploadComputer} />
|
|
219
223
|
</CapButton>
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
224
|
+
{channel !== WHATSAPP && (
|
|
225
|
+
<CapButton
|
|
226
|
+
className="dragger-button gallery-select"
|
|
227
|
+
type="secondary"
|
|
228
|
+
onClick={onGalleryClick}
|
|
229
|
+
>
|
|
230
|
+
<FormattedMessage {...messages.uploadGallery} />
|
|
231
|
+
</CapButton>
|
|
232
|
+
)}
|
|
233
|
+
</CapUploader.CapDragger>
|
|
234
|
+
<CapError type="error" className="upload-image-error">
|
|
235
|
+
{isImageError}
|
|
236
|
+
</CapError>
|
|
225
237
|
</>
|
|
226
238
|
);
|
|
227
239
|
}
|
|
@@ -242,26 +254,26 @@ function CapImageUpload(props) {
|
|
|
242
254
|
return (
|
|
243
255
|
<div style={style} className="cap-custom-image-upload">
|
|
244
256
|
<WithLabel
|
|
245
|
-
key=
|
|
257
|
+
key="with-label"
|
|
246
258
|
ifError={!!isImageError}
|
|
247
259
|
|
|
248
|
-
|
|
249
|
-
<form encType="multipart/form-data" id=
|
|
260
|
+
/>
|
|
261
|
+
<form encType="multipart/form-data" id="form" className={className}>
|
|
250
262
|
<input
|
|
251
|
-
key=
|
|
263
|
+
key="imgFile"
|
|
252
264
|
style={{ display: 'none' }}
|
|
253
|
-
id="
|
|
265
|
+
id="imageFileName"
|
|
254
266
|
type="file"
|
|
255
267
|
onChange={(e) => uploadImages(e, { files: e.target.files })}
|
|
256
268
|
accept={supportedExtensions || "image/*"}
|
|
257
|
-
|
|
269
|
+
/>
|
|
258
270
|
{getImageSection()}
|
|
259
271
|
<CapDrawer
|
|
260
272
|
content={getGalleryDrawerContent()}
|
|
261
273
|
visible={isDrawerRequired}
|
|
262
274
|
width={430}
|
|
263
275
|
onClose={() => updateDrawerRequirement(false)}
|
|
264
|
-
|
|
276
|
+
/>
|
|
265
277
|
</form>
|
|
266
278
|
<div className="image-info-wrapper">
|
|
267
279
|
{channel === WHATSAPP && (
|
|
@@ -309,6 +321,7 @@ CapImageUpload.propTypes = {
|
|
|
309
321
|
index: PropTypes.number,
|
|
310
322
|
channel: PropTypes.string,
|
|
311
323
|
channelSpecificStyle: PropTypes.bool,
|
|
324
|
+
disableAutoRestore: PropTypes.bool,
|
|
312
325
|
};
|
|
313
326
|
|
|
314
327
|
export default injectIntl(CapImageUpload);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { FormattedMessage } from 'react-intl';
|
|
3
|
+
import messages from './messages';
|
|
4
|
+
|
|
5
|
+
export const DEEP_LINK = 'DEEP_LINK';
|
|
6
|
+
export const EXTERNAL_URL = 'EXTERNAL_URL';
|
|
7
|
+
export const WEBSITE = 'WEBSITE';
|
|
8
|
+
|
|
9
|
+
export const CTA_OPTIONS = [
|
|
10
|
+
{
|
|
11
|
+
key: DEEP_LINK,
|
|
12
|
+
value: DEEP_LINK,
|
|
13
|
+
label: <FormattedMessage {...messages.ctaLinkTypeDeep} />,
|
|
14
|
+
tooltipLabel: <FormattedMessage {...messages.ctaOptionDisabledTooltipDeepLink} />,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
key: EXTERNAL_URL,
|
|
18
|
+
value: EXTERNAL_URL,
|
|
19
|
+
label: <FormattedMessage {...messages.ctaLinkTypeExternal} />,
|
|
20
|
+
tooltipLabel: <FormattedMessage {...messages.ctaOptionDisabledTooltipExternalLink} />,
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
export const BTN_MAX_LENGTH = 20;
|
|
25
|
+
export const URL_MAX_LENGTH = 2000;
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import React, { useState, useEffect, useRef } from "react";
|
|
2
|
+
import { injectIntl } from "react-intl";
|
|
3
|
+
import "react-phone-input-2/lib/style.css";
|
|
4
|
+
import cloneDeep from "lodash/cloneDeep";
|
|
5
|
+
import CapHeading from "@capillarytech/cap-ui-library/CapHeading";
|
|
6
|
+
import CapColumn from "@capillarytech/cap-ui-library/CapColumn";
|
|
7
|
+
import CapInput from "@capillarytech/cap-ui-library/CapInput";
|
|
8
|
+
import CapButton from "@capillarytech/cap-ui-library/CapButton";
|
|
9
|
+
import CapRow from "@capillarytech/cap-ui-library/CapRow";
|
|
10
|
+
import CapLabel from "@capillarytech/cap-ui-library/CapLabel";
|
|
11
|
+
import CapIcon from "@capillarytech/cap-ui-library/CapIcon";
|
|
12
|
+
import CapTooltip from "@capillarytech/cap-ui-library/CapTooltip";
|
|
13
|
+
import CapSelect from "@capillarytech/cap-ui-library/CapSelect";
|
|
14
|
+
import CapTag from '@capillarytech/cap-ui-library/CapTag';
|
|
15
|
+
import globalMessages from "../../v2Containers/Cap/messages";
|
|
16
|
+
import inAppMsg from "../../v2Containers/InApp/messages";
|
|
17
|
+
import messages from "./messages";
|
|
18
|
+
import './index.scss';
|
|
19
|
+
import {
|
|
20
|
+
isUrl,
|
|
21
|
+
isValidText,
|
|
22
|
+
} from "../../v2Containers/Line/Container/Wrapper/utils";
|
|
23
|
+
import {
|
|
24
|
+
BTN_MAX_LENGTH,
|
|
25
|
+
DEEP_LINK,
|
|
26
|
+
EXTERNAL_URL,
|
|
27
|
+
CTA_OPTIONS,
|
|
28
|
+
URL_MAX_LENGTH,
|
|
29
|
+
} from "./constants";
|
|
30
|
+
import { PRIMARY } from "../../v2Containers/MobilePushNew/constants";
|
|
31
|
+
|
|
32
|
+
export const CapMpushCTA = (props) => {
|
|
33
|
+
const {
|
|
34
|
+
intl,
|
|
35
|
+
ctaData = [],
|
|
36
|
+
updateHandler,
|
|
37
|
+
deleteHandler,
|
|
38
|
+
deepLink,
|
|
39
|
+
buttonType,
|
|
40
|
+
} = props;
|
|
41
|
+
const { formatMessage } = intl;
|
|
42
|
+
|
|
43
|
+
const [urlError, setUrlError] = useState(false);
|
|
44
|
+
const [buttonError, setButtonError] = useState(false);
|
|
45
|
+
const [ctaDeepLinkValue, setCtaDeepLinkValue] = useState(ctaData[0]?.url || "");
|
|
46
|
+
const [localUrlValues, setLocalUrlValues] = useState({});
|
|
47
|
+
// Track which URLs have been manually cleared to prevent restoration
|
|
48
|
+
const clearedUrlsRef = useRef(new Set());
|
|
49
|
+
const selectedDeepLink = deepLink?.find(link => link?.value === ctaData[0]?.url);
|
|
50
|
+
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
const urlValues = {};
|
|
53
|
+
ctaData.forEach((cta) => {
|
|
54
|
+
if (cta && typeof cta.index !== 'undefined') {
|
|
55
|
+
// Don't restore URLs that have been manually cleared
|
|
56
|
+
const wasManuallyCleared = clearedUrlsRef.current.has(cta.index);
|
|
57
|
+
urlValues[cta.index] = wasManuallyCleared ? '' : (cta.url || '');
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
setLocalUrlValues(urlValues);
|
|
63
|
+
}, [ctaData]);
|
|
64
|
+
|
|
65
|
+
const updateHelper = (type, value, index) => {
|
|
66
|
+
let clonedCta = cloneDeep(ctaData[index]);
|
|
67
|
+
clonedCta = {
|
|
68
|
+
...clonedCta,
|
|
69
|
+
[type]: value,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
if (type === 'url') {
|
|
73
|
+
setLocalUrlValues(prev => ({
|
|
74
|
+
...prev,
|
|
75
|
+
[index]: value
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
updateHandler(clonedCta, index);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const renderLength = (len, max) => (
|
|
83
|
+
<CapHeading
|
|
84
|
+
type="label1"
|
|
85
|
+
className="inapp-render-btn-length"
|
|
86
|
+
>
|
|
87
|
+
{formatMessage(inAppMsg.templateMessageLength, {
|
|
88
|
+
currentLength: len,
|
|
89
|
+
maxLength: max,
|
|
90
|
+
})}
|
|
91
|
+
</CapHeading>
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
const onButtonTextChange = ({ target }) => {
|
|
96
|
+
const { value, id } = target;
|
|
97
|
+
let errorMessage = "";
|
|
98
|
+
if (!isValidText(value)) {
|
|
99
|
+
errorMessage = formatMessage(messages.ctaButtonErrorMessage);
|
|
100
|
+
}
|
|
101
|
+
setButtonError(errorMessage);
|
|
102
|
+
updateHelper("text", value, id);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const onUrlTypeChange = (value, index) => {
|
|
106
|
+
// Mark this URL as manually cleared to prevent restoration
|
|
107
|
+
clearedUrlsRef.current.add(index);
|
|
108
|
+
|
|
109
|
+
// Clear the URL field when switching between link types
|
|
110
|
+
updateHelper('url', '', index);
|
|
111
|
+
|
|
112
|
+
// Immediately clear the local URL state for this button
|
|
113
|
+
setLocalUrlValues(prev => ({
|
|
114
|
+
...prev,
|
|
115
|
+
[index]: ''
|
|
116
|
+
}));
|
|
117
|
+
|
|
118
|
+
// Also clear the local deep link state
|
|
119
|
+
setCtaDeepLinkValue('');
|
|
120
|
+
// Clear any URL errors
|
|
121
|
+
setUrlError(false);
|
|
122
|
+
// Update the URL type
|
|
123
|
+
updateHelper('urlType', value, index);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const onUrlChange = ({ target }) => {
|
|
127
|
+
const { value, dataset } = target;
|
|
128
|
+
const index = parseInt(dataset.index, 10);
|
|
129
|
+
|
|
130
|
+
// If user is typing a new URL, remove from cleared set
|
|
131
|
+
if (value && value.trim() !== '') {
|
|
132
|
+
clearedUrlsRef.current.delete(index);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let errorMessage = false;
|
|
136
|
+
if (!isUrl(value)) {
|
|
137
|
+
errorMessage = formatMessage(messages.ctaWebsiteUrlErrorMessage);
|
|
138
|
+
}
|
|
139
|
+
setUrlError(errorMessage);
|
|
140
|
+
|
|
141
|
+
// Update local state immediately for instant UI feedback
|
|
142
|
+
setLocalUrlValues(prev => ({
|
|
143
|
+
...prev,
|
|
144
|
+
[index]: value
|
|
145
|
+
}));
|
|
146
|
+
|
|
147
|
+
updateHelper('url', value, index);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const onDeepLinkSelect = (value, index) => {
|
|
151
|
+
setCtaDeepLinkValue(value);
|
|
152
|
+
let errorMessage = false;
|
|
153
|
+
if (!isUrl(value)) {
|
|
154
|
+
errorMessage = formatMessage(messages.ctaWebsiteUrlErrorMessage);
|
|
155
|
+
}
|
|
156
|
+
setUrlError(errorMessage);
|
|
157
|
+
updateHelper('url', value, index);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const ctaSaveDisabled = (index) => {
|
|
161
|
+
const { urlType, text, url } = ctaData[index] || {};
|
|
162
|
+
if (text === "" || buttonError) {
|
|
163
|
+
return true;
|
|
164
|
+
} else if (urlType === DEEP_LINK && (ctaDeepLinkValue === "" || urlError)) {
|
|
165
|
+
return true;
|
|
166
|
+
} else if (urlType === EXTERNAL_URL && (url === "" || urlError)) {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const isSavedCta = (index, saved) => {
|
|
172
|
+
updateHelper("isSaved", saved, index);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const renderedContent = () => {
|
|
176
|
+
const renderArray = [];
|
|
177
|
+
const filteredCtaData = ctaData?.filter((cta) => {
|
|
178
|
+
if (buttonType === PRIMARY) {
|
|
179
|
+
return cta.index === 0;
|
|
180
|
+
} else {
|
|
181
|
+
return cta.index === 1;
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
filteredCtaData?.forEach((cta) => {
|
|
186
|
+
const { index, text, url, urlType, isSaved } = cta || {};
|
|
187
|
+
if (isSaved) {
|
|
188
|
+
renderArray.push(
|
|
189
|
+
<CapRow
|
|
190
|
+
className="cap-inapp-saved-cta"
|
|
191
|
+
align="middle"
|
|
192
|
+
type="flex"
|
|
193
|
+
>
|
|
194
|
+
<CapRow className="inapp-cta-row">
|
|
195
|
+
<CapColumn>
|
|
196
|
+
<CapIcon size="s" type={'launch'} className="btn-icon" />
|
|
197
|
+
</CapColumn>
|
|
198
|
+
<CapColumn className="btn-text">
|
|
199
|
+
<CapLabel type="label2" className="inapp-saved-cta-button-text">
|
|
200
|
+
{text}
|
|
201
|
+
</CapLabel>
|
|
202
|
+
</CapColumn>
|
|
203
|
+
</CapRow>
|
|
204
|
+
<CapColumn >
|
|
205
|
+
{urlType === DEEP_LINK ?
|
|
206
|
+
<CapTag className="cta-type-label">{formatMessage(messages.urlDeepLink)}</CapTag>
|
|
207
|
+
: <CapTag className="cta-type-label">{formatMessage(messages.urlExternalLink)}</CapTag>}
|
|
208
|
+
</CapColumn>
|
|
209
|
+
<CapColumn>
|
|
210
|
+
{urlType === DEEP_LINK ? selectedDeepLink?.label : ''}
|
|
211
|
+
</CapColumn>
|
|
212
|
+
{(
|
|
213
|
+
<CapRow className="cta-action-grp">
|
|
214
|
+
<CapColumn
|
|
215
|
+
className="inapp-saved-cta-edit-icon"
|
|
216
|
+
onClick={() => isSavedCta(index, false)}
|
|
217
|
+
>
|
|
218
|
+
<CapIcon size="s" type="edit" className="cta-action" />
|
|
219
|
+
</CapColumn>
|
|
220
|
+
<CapColumn onClick={() => deleteHandler(index)} className="cta-del-icon">
|
|
221
|
+
<CapIcon size="s" type="delete" className="cta-action" ariaLabel="delete-cta-icon" />
|
|
222
|
+
</CapColumn>
|
|
223
|
+
</CapRow>
|
|
224
|
+
)}
|
|
225
|
+
</CapRow>
|
|
226
|
+
);
|
|
227
|
+
} else {
|
|
228
|
+
renderArray.push(
|
|
229
|
+
<CapRow
|
|
230
|
+
className="cap-inapp-cta"
|
|
231
|
+
id={`cap-inapp-cta-${index}`}
|
|
232
|
+
>
|
|
233
|
+
<CapColumn>
|
|
234
|
+
<CapHeading type="h4" className="cta-label">
|
|
235
|
+
{buttonType === PRIMARY ? formatMessage(messages.ctaButtonText) : formatMessage(messages.ctaButtonTextSecondary)}
|
|
236
|
+
</CapHeading>
|
|
237
|
+
<CapInput
|
|
238
|
+
id={index}
|
|
239
|
+
onChange={onButtonTextChange}
|
|
240
|
+
placeholder={formatMessage(messages.ctaButtonTextPlaceholder)}
|
|
241
|
+
value={text}
|
|
242
|
+
maxLength={BTN_MAX_LENGTH}
|
|
243
|
+
errorMessage={buttonError}
|
|
244
|
+
/>
|
|
245
|
+
{renderLength(text.length, BTN_MAX_LENGTH)}
|
|
246
|
+
</CapColumn>
|
|
247
|
+
<CapColumn className="inapp-cta-buttons">
|
|
248
|
+
<CapRow style={{ width: '30%', marginRight: '10px' }}>
|
|
249
|
+
<CapHeading type="h4" className="cta-label">
|
|
250
|
+
{formatMessage(messages.ctaType)}
|
|
251
|
+
</CapHeading>
|
|
252
|
+
<CapSelect.CapCustomSelect
|
|
253
|
+
id="inapp-cta-type"
|
|
254
|
+
key="mpush-cta-type"
|
|
255
|
+
options={CTA_OPTIONS || []}
|
|
256
|
+
onChange={(value) => onUrlTypeChange(value, index)}
|
|
257
|
+
value={urlType}
|
|
258
|
+
/>
|
|
259
|
+
</CapRow>
|
|
260
|
+
{urlType === DEEP_LINK && (
|
|
261
|
+
<CapRow style={{ width: '70%' }}>
|
|
262
|
+
<CapHeading type="h4">
|
|
263
|
+
{formatMessage(messages.urlDeepLink)}
|
|
264
|
+
</CapHeading>
|
|
265
|
+
<CapSelect.CapCustomSelect
|
|
266
|
+
id="inapp-deep-link-type"
|
|
267
|
+
key="mpush-deep-link-type"
|
|
268
|
+
placeholder={formatMessage(messages.ctaDeepLinkOptionsPlaceholder)}
|
|
269
|
+
options={deepLink || []}
|
|
270
|
+
onChange={(value) => onDeepLinkSelect(value, index)}
|
|
271
|
+
value={ctaDeepLinkValue}
|
|
272
|
+
/>
|
|
273
|
+
</CapRow>
|
|
274
|
+
|
|
275
|
+
)}
|
|
276
|
+
{urlType === EXTERNAL_URL && (
|
|
277
|
+
<CapRow style={{ width: '70%' }}>
|
|
278
|
+
<CapHeading type="h4">
|
|
279
|
+
{formatMessage(messages.urlExternalLink)}
|
|
280
|
+
</CapHeading>
|
|
281
|
+
<CapInput
|
|
282
|
+
id="inapp-cta-external-link"
|
|
283
|
+
className="inapp-cta-external-link"
|
|
284
|
+
onChange={onUrlChange}
|
|
285
|
+
placeholder={formatMessage(messages.ctaExternalLinkPlaceholder)}
|
|
286
|
+
value={localUrlValues[index] || ''}
|
|
287
|
+
size="large"
|
|
288
|
+
maxLength={URL_MAX_LENGTH}
|
|
289
|
+
errorMessage={urlError}
|
|
290
|
+
data-index={index}
|
|
291
|
+
/>
|
|
292
|
+
</CapRow>
|
|
293
|
+
)}
|
|
294
|
+
</CapColumn>
|
|
295
|
+
<CapRow className="inapp-cta-save-delete-btn">
|
|
296
|
+
<CapTooltip
|
|
297
|
+
title={
|
|
298
|
+
ctaSaveDisabled(index)
|
|
299
|
+
? formatMessage(messages.ctaSaveDisabled)
|
|
300
|
+
: ""
|
|
301
|
+
}
|
|
302
|
+
placement={"bottom"}
|
|
303
|
+
>
|
|
304
|
+
<div className="button-disabled-tooltip-wrapper">
|
|
305
|
+
<CapButton
|
|
306
|
+
onClick={() => isSavedCta(index, true)}
|
|
307
|
+
disabled={ctaSaveDisabled(index)}
|
|
308
|
+
className="inapp-cta-save-btn"
|
|
309
|
+
>
|
|
310
|
+
{formatMessage(globalMessages.save)}
|
|
311
|
+
</CapButton>
|
|
312
|
+
</div>
|
|
313
|
+
</CapTooltip>
|
|
314
|
+
<CapButton
|
|
315
|
+
onClick={() => deleteHandler(index)}
|
|
316
|
+
className="inapp-cta-delete-btn"
|
|
317
|
+
type="secondary"
|
|
318
|
+
>
|
|
319
|
+
{formatMessage(globalMessages.delete)}
|
|
320
|
+
</CapButton>
|
|
321
|
+
</CapRow>
|
|
322
|
+
</CapRow>
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
return renderArray;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
return renderedContent();
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
export default injectIntl(CapMpushCTA);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
@import '~@capillarytech/cap-ui-library/styles/_variables.scss';
|
|
2
|
+
|
|
3
|
+
.cap-inapp-cta {
|
|
4
|
+
margin-top: $CAP_SPACE_16;
|
|
5
|
+
margin-left: 1.6rem;
|
|
6
|
+
padding: $CAP_SPACE_16 $CAP_SPACE_24;
|
|
7
|
+
border: 1px solid $CAP_G06;
|
|
8
|
+
border-radius: $CAP_SPACE_04;
|
|
9
|
+
|
|
10
|
+
.inapp-cta-buttons {
|
|
11
|
+
margin-top: $CAP_SPACE_12;
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: row;
|
|
14
|
+
align-items: flex-end;
|
|
15
|
+
justify-content: space-between;
|
|
16
|
+
|
|
17
|
+
.inapp-render-btn-length {
|
|
18
|
+
margin-top: $CAP_SPACE_12;
|
|
19
|
+
width: 90%;
|
|
20
|
+
margin-left: 52%;
|
|
21
|
+
margin-bottom: $CAP_SPACE_12;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#inapp-deep-link-type {
|
|
26
|
+
width: 100%;
|
|
27
|
+
margin-top: $CAP_SPACE_12;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.inapp-cta-external-link {
|
|
31
|
+
margin-top: $CAP_SPACE_12;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.inapp-cta-save-delete-btn {
|
|
35
|
+
margin-top: $CAP_SPACE_16;
|
|
36
|
+
|
|
37
|
+
.inapp-cta-save-btn {
|
|
38
|
+
margin-right: $CAP_SPACE_04;
|
|
39
|
+
}
|
|
40
|
+
.inapp-cta-delete-btn {
|
|
41
|
+
margin-left: $CAP_SPACE_04;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.cap-inapp-saved-cta {
|
|
48
|
+
border: solid 1px $CAP_G06;
|
|
49
|
+
margin-left: $CAP_SPACE_20;
|
|
50
|
+
display: flex;
|
|
51
|
+
justify-content: space-between;
|
|
52
|
+
padding: $CAP_SPACE_08 $CAP_SPACE_12;
|
|
53
|
+
border-radius: $CAP_SPACE_04;
|
|
54
|
+
margin-top: $CAP_SPACE_12;
|
|
55
|
+
height: $CAP_SPACE_40;
|
|
56
|
+
|
|
57
|
+
.inapp-saved-cta-button-text {
|
|
58
|
+
font-weight: 500;
|
|
59
|
+
padding-left: $CAP_SPACE_16;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.inapp-saved-cta-edit-icon {
|
|
63
|
+
margin-left: 4.286rem;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
.cta-label {
|
|
67
|
+
margin-bottom: $CAP_SPACE_08;
|
|
68
|
+
}
|
|
69
|
+
.cta-type-label {
|
|
70
|
+
border-radius: 1rem;
|
|
71
|
+
background-color: $CAP_G08;
|
|
72
|
+
width: $CAP_SPACE_80;
|
|
73
|
+
height: $CAP_SPACE_24 !important;
|
|
74
|
+
align-items: center;
|
|
75
|
+
justify-content: center;
|
|
76
|
+
}
|
|
77
|
+
.cta-action {
|
|
78
|
+
cursor: pointer;
|
|
79
|
+
}
|
|
80
|
+
.inapp-cta-row {
|
|
81
|
+
display: flex;
|
|
82
|
+
align-items: center;
|
|
83
|
+
}
|
|
84
|
+
.cta-action-grp {
|
|
85
|
+
display: flex;
|
|
86
|
+
}
|
|
87
|
+
.cta-del-icon {
|
|
88
|
+
padding-left: 1rem;
|
|
89
|
+
}
|
|
90
|
+
.btn-text {
|
|
91
|
+
margin-left: $CAP_SPACE_08;
|
|
92
|
+
}
|
|
93
|
+
.btn-icon {
|
|
94
|
+
color: $CAP_G06;
|
|
95
|
+
}
|