@capillarytech/creatives-library 8.0.128 → 8.0.129

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.
Files changed (68) hide show
  1. package/containers/App/constants.js +1 -0
  2. package/package.json +1 -1
  3. package/services/api.js +1 -1
  4. package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +8 -3
  5. package/tests/integration/TemplateCreation/api-response.js +5 -0
  6. package/tests/integration/TemplateCreation/msw-handler.js +42 -63
  7. package/utils/common.js +7 -0
  8. package/utils/commonUtils.js +2 -6
  9. package/utils/createPayload.js +405 -0
  10. package/utils/tests/createPayload.test.js +785 -0
  11. package/v2Components/CapImageUpload/index.js +59 -46
  12. package/v2Components/CapInAppCTA/index.js +1 -0
  13. package/v2Components/CapMpushCTA/constants.js +25 -0
  14. package/v2Components/CapMpushCTA/index.js +402 -0
  15. package/v2Components/CapMpushCTA/index.scss +95 -0
  16. package/v2Components/CapMpushCTA/messages.js +101 -0
  17. package/v2Components/CapTagList/index.js +177 -120
  18. package/v2Components/CapVideoUpload/constants.js +3 -0
  19. package/v2Components/CapVideoUpload/index.js +167 -110
  20. package/v2Components/CapVideoUpload/messages.js +16 -0
  21. package/v2Components/Carousel/index.js +15 -13
  22. package/v2Components/ErrorInfoNote/style.scss +1 -0
  23. package/v2Components/MobilePushPreviewV2/index.js +37 -5
  24. package/v2Components/TemplatePreview/_templatePreview.scss +114 -72
  25. package/v2Components/TemplatePreview/assets/images/Android _ With date and time.svg +29 -0
  26. package/v2Components/TemplatePreview/assets/images/android.svg +9 -0
  27. package/v2Components/TemplatePreview/assets/images/iOS _ With date and time.svg +26 -0
  28. package/v2Components/TemplatePreview/assets/images/ios.svg +9 -0
  29. package/v2Components/TemplatePreview/index.js +178 -50
  30. package/v2Components/TemplatePreview/messages.js +4 -0
  31. package/v2Containers/CreativesContainer/SlideBoxContent.js +127 -62
  32. package/v2Containers/CreativesContainer/index.js +191 -136
  33. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -22
  34. package/v2Containers/InApp/constants.js +1 -0
  35. package/v2Containers/InApp/index.js +13 -13
  36. package/v2Containers/MobilePush/Create/index.js +1 -0
  37. package/v2Containers/MobilePush/commonMethods.js +7 -14
  38. package/v2Containers/MobilePushNew/actions.js +116 -0
  39. package/v2Containers/MobilePushNew/components/CtaButtons.js +181 -0
  40. package/v2Containers/MobilePushNew/components/MediaUploaders.js +834 -0
  41. package/v2Containers/MobilePushNew/components/PlatformContentFields.js +345 -0
  42. package/v2Containers/MobilePushNew/components/index.js +5 -0
  43. package/v2Containers/MobilePushNew/components/tests/CtaButtons.test.js +798 -0
  44. package/v2Containers/MobilePushNew/components/tests/MediaUploaders.test.js +2114 -0
  45. package/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +343 -0
  46. package/v2Containers/MobilePushNew/constants.js +115 -0
  47. package/v2Containers/MobilePushNew/hooks/tests/usePlatformSync.test.js +1299 -0
  48. package/v2Containers/MobilePushNew/hooks/tests/useUpload.test.js +1223 -0
  49. package/v2Containers/MobilePushNew/hooks/usePlatformSync.js +246 -0
  50. package/v2Containers/MobilePushNew/hooks/useUpload.js +726 -0
  51. package/v2Containers/MobilePushNew/index.js +3412 -0
  52. package/v2Containers/MobilePushNew/index.scss +308 -0
  53. package/v2Containers/MobilePushNew/messages.js +242 -0
  54. package/v2Containers/MobilePushNew/reducer.js +160 -0
  55. package/v2Containers/MobilePushNew/sagas.js +198 -0
  56. package/v2Containers/MobilePushNew/selectors.js +55 -0
  57. package/v2Containers/MobilePushNew/tests/reducer.test.js +741 -0
  58. package/v2Containers/MobilePushNew/tests/sagas.test.js +863 -0
  59. package/v2Containers/MobilePushNew/tests/selectors.test.js +425 -0
  60. package/v2Containers/MobilePushNew/tests/utils.test.js +322 -0
  61. package/v2Containers/MobilePushNew/utils.js +33 -0
  62. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +5 -5
  63. package/v2Containers/TagList/index.js +56 -10
  64. package/v2Containers/Templates/_templates.scss +101 -1
  65. package/v2Containers/Templates/index.js +147 -35
  66. package/v2Containers/Templates/messages.js +8 -0
  67. package/v2Containers/Templates/sagas.js +2 -0
  68. package/v2Containers/Whatsapp/constants.js +1 -0
@@ -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 { FACEBOOK, INAPP, RCS, WHATSAPP, VIBER } from "../../v2Containers/CreativesContainer/constants";
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
- } = intl || {};
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 = '', karixFileHandle = '' } = get(imageDataObj, 'metaInfo', {});
58
- updateImageSrc(secure_file_path, karixFileHandle);
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
- [imageSrc],
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 {secure_file_path: image, width, height, file_size: size} = get(imageTemplate, 'metaInfo', {});
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
- <CapUploader.CapDragger
199
- customRequest={capUploaderCustomRequest}
200
- className="form-builder-dragger grey-background"
201
- showUploadList={!isImageError}
202
- >
203
- <CapHeading className="dragger-title" type="h7">
204
- <FormattedMessage {...messages.dragAndDrop} />
205
- </CapHeading>
206
- <CapHeading className="dragger-or" type="label6">
207
- <FormattedMessage {...messages.or} />
208
- </CapHeading>
209
- <CapButton className="dragger-button upload-image" type="secondary">
210
- <FormattedMessage {...messages.uploadComputer} />
211
- </CapButton>
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
- </CapUploader.CapDragger>
222
- <CapError type="error" className="upload-image-error">
223
- {isImageError}
224
- </CapError>
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={`with-label`}
257
+ key="with-label"
246
258
  ifError={!!isImageError}
247
259
 
248
- />
249
- <form encType="multipart/form-data" id={`form`} className={className}>
260
+ />
261
+ <form encType="multipart/form-data" id="form" className={className}>
250
262
  <input
251
- key={`imgFile`}
263
+ key="imgFile"
252
264
  style={{ display: 'none' }}
253
- id="fileName"
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);
@@ -38,6 +38,7 @@ export const CapInAppCTA = (props) => {
38
38
  deleteHandler,
39
39
  isEditFlow,
40
40
  deepLink,
41
+ channel,
41
42
  } = props;
42
43
  const { formatMessage } = intl;
43
44
 
@@ -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,402 @@
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 [deepLinkKeysError, setDeepLinkKeysError] = useState(false);
46
+ const [ctaDeepLinkValue, setCtaDeepLinkValue] = useState(ctaData[0]?.url || "");
47
+ const [ctaDeepLinkKeysValue, setCtaDeepLinkKeysValue] = useState(ctaData[0]?.deepLinkKeys || []);
48
+ const [localUrlValues, setLocalUrlValues] = useState({});
49
+ // Track which URLs have been manually cleared to prevent restoration
50
+ const clearedUrlsRef = useRef(new Set());
51
+ const selectedDeepLink = deepLink?.find((link) => link?.value === ctaData[0]?.url);
52
+ const deepLinkKeysFromSelection = selectedDeepLink?.keys;
53
+
54
+ // Handle deep link keys value - could be array or string
55
+ let deepLinkKeysArray = [];
56
+ if (Array.isArray(ctaDeepLinkKeysValue)) {
57
+ deepLinkKeysArray = ctaDeepLinkKeysValue;
58
+ } else if (ctaDeepLinkKeysValue) {
59
+ deepLinkKeysArray = [ctaDeepLinkKeysValue];
60
+ }
61
+
62
+ let deepLinkKeysFromSelectionArray = [];
63
+ if (Array.isArray(deepLinkKeysFromSelection)) {
64
+ deepLinkKeysFromSelectionArray = deepLinkKeysFromSelection;
65
+ } else if (deepLinkKeysFromSelection) {
66
+ deepLinkKeysFromSelectionArray = [deepLinkKeysFromSelection];
67
+ }
68
+
69
+ useEffect(() => {
70
+ const urlValues = {};
71
+ ctaData.forEach((cta) => {
72
+ if (cta && typeof cta.index !== 'undefined') {
73
+ // Don't restore URLs that have been manually cleared
74
+ const wasManuallyCleared = clearedUrlsRef.current.has(cta.index);
75
+ urlValues[cta.index] = wasManuallyCleared ? '' : (cta.url || '');
76
+
77
+
78
+ }
79
+ });
80
+ setLocalUrlValues(urlValues);
81
+ }, [ctaData]);
82
+
83
+ const updateHelper = (type, value, index) => {
84
+ let clonedCta = cloneDeep(ctaData[index]);
85
+ clonedCta = {
86
+ ...clonedCta,
87
+ [type]: value,
88
+ };
89
+
90
+ if (type === 'url') {
91
+ setLocalUrlValues(prev => ({
92
+ ...prev,
93
+ [index]: value
94
+ }));
95
+ }
96
+
97
+ updateHandler(clonedCta, index);
98
+ };
99
+
100
+ const renderLength = (len, max) => (
101
+ <CapHeading
102
+ type="label1"
103
+ className="inapp-render-btn-length"
104
+ >
105
+ {formatMessage(inAppMsg.templateMessageLength, {
106
+ currentLength: len,
107
+ maxLength: max,
108
+ })}
109
+ </CapHeading>
110
+ );
111
+
112
+
113
+ const onButtonTextChange = ({ target }) => {
114
+ const { value, id } = target;
115
+ let errorMessage = "";
116
+ if (!isValidText(value)) {
117
+ errorMessage = formatMessage(messages.ctaButtonErrorMessage);
118
+ }
119
+ setButtonError(errorMessage);
120
+ updateHelper("text", value, id);
121
+ };
122
+
123
+ const onUrlTypeChange = (value, index) => {
124
+ // Mark this URL as manually cleared to prevent restoration
125
+ clearedUrlsRef.current.add(index);
126
+
127
+ // Clear the URL field when switching between link types
128
+ updateHelper('url', '', index);
129
+
130
+ // Immediately clear the local URL state for this button
131
+ setLocalUrlValues(prev => ({
132
+ ...prev,
133
+ [index]: ''
134
+ }));
135
+
136
+ // Also clear the local deep link state
137
+ setCtaDeepLinkValue('');
138
+ // Clear any URL errors
139
+ setUrlError(false);
140
+ // Update the URL type
141
+ updateHelper('urlType', value, index);
142
+ };
143
+
144
+ const onUrlChange = ({ target }) => {
145
+ const { value, dataset } = target;
146
+ const index = parseInt(dataset.index, 10);
147
+
148
+ // If user is typing a new URL, remove from cleared set
149
+ if (value && value.trim() !== '') {
150
+ clearedUrlsRef.current.delete(index);
151
+ }
152
+
153
+ let errorMessage = false;
154
+ if (!isUrl(value)) {
155
+ errorMessage = formatMessage(messages.ctaWebsiteUrlErrorMessage);
156
+ }
157
+ setUrlError(errorMessage);
158
+
159
+ // Update local state immediately for instant UI feedback
160
+ setLocalUrlValues(prev => ({
161
+ ...prev,
162
+ [index]: value
163
+ }));
164
+
165
+ updateHelper('url', value, index);
166
+ };
167
+
168
+ const onDeepLinkSelect = (value, index) => {
169
+ setCtaDeepLinkValue(value);
170
+ let errorMessage = false;
171
+ if (!isUrl(value)) {
172
+ errorMessage = formatMessage(messages.ctaWebsiteUrlErrorMessage);
173
+ }
174
+ setUrlError(errorMessage);
175
+ updateHelper('url', value, index);
176
+ };
177
+
178
+ const onDeepLinkKeysChange = ({ target }) => {
179
+ const { value, dataset } = target;
180
+ const index = parseInt(dataset.index, 10);
181
+ // Parse comma-separated values into array
182
+ const keysArray = value.split(',').map((key) => key.trim()).filter((key) => key.length > 0);
183
+ setCtaDeepLinkKeysValue(keysArray);
184
+ setDeepLinkKeysError(keysArray.length === 0);
185
+ updateHelper('deepLinkKeys', keysArray, index);
186
+ };
187
+
188
+ const ctaSaveDisabled = (index) => {
189
+ const { urlType, text, url } = ctaData[index] || {};
190
+ if (text === "" || buttonError) {
191
+ return true;
192
+ } else if (urlType === DEEP_LINK && (ctaDeepLinkValue === "" || urlError)) {
193
+ return true;
194
+ } else if (urlType === DEEP_LINK && ctaDeepLinkValue && deepLinkKeysFromSelectionArray.length > 0 && (!Array.isArray(ctaDeepLinkKeysValue) || ctaDeepLinkKeysValue.length === 0 || deepLinkKeysError)) {
195
+ return true;
196
+ } else if (urlType === EXTERNAL_URL && (url === "" || urlError)) {
197
+ return true;
198
+ }
199
+ };
200
+
201
+ const isSavedCta = (index, saved) => {
202
+ updateHelper("isSaved", saved, index);
203
+ };
204
+
205
+ const renderedContent = () => {
206
+ const renderArray = [];
207
+ const filteredCtaData = ctaData?.filter((cta) => {
208
+ if (buttonType === PRIMARY) {
209
+ return cta.index === 0;
210
+ } else {
211
+ return cta.index === 1;
212
+ }
213
+ });
214
+
215
+ filteredCtaData?.forEach((cta) => {
216
+ const { index, text, url, urlType, isSaved } = cta || {};
217
+ if (isSaved) {
218
+ renderArray.push(
219
+ <CapRow
220
+ className="cap-inapp-saved-cta"
221
+ align="middle"
222
+ type="flex"
223
+ >
224
+ <CapRow className="inapp-cta-row">
225
+ <CapColumn>
226
+ <CapIcon size="s" type={'launch'} className="btn-icon" />
227
+ </CapColumn>
228
+ <CapColumn className="btn-text">
229
+ <CapLabel type="label2" className="inapp-saved-cta-button-text">
230
+ {text}
231
+ </CapLabel>
232
+ </CapColumn>
233
+ </CapRow>
234
+ <CapColumn >
235
+ {urlType === DEEP_LINK ?
236
+ <CapTag className="cta-type-label">{formatMessage(messages.urlDeepLink)}</CapTag>
237
+ : <CapTag className="cta-type-label">{formatMessage(messages.urlExternalLink)}</CapTag>}
238
+ </CapColumn>
239
+ <CapColumn>
240
+ {urlType === DEEP_LINK ? selectedDeepLink?.label : ''}
241
+ </CapColumn>
242
+ <CapColumn>
243
+ {urlType === DEEP_LINK && deepLinkKeysFromSelectionArray.length > 0 && (ctaData[index]?.deepLinkKeys || selectedDeepLink?.keys) ? (
244
+ <CapLabel type="label2" className="inapp-saved-cta-deep-link-keys">
245
+ {(() => {
246
+ const keys = selectedDeepLink?.keys || ctaData[index].deepLinkKeys;
247
+ if (Array.isArray(keys)) {
248
+ return keys.join(', ');
249
+ }
250
+ return keys;
251
+ })()}
252
+ </CapLabel>
253
+ ) : ''}
254
+ </CapColumn>
255
+ {(
256
+ <CapRow className="cta-action-grp">
257
+ <CapColumn
258
+ className="inapp-saved-cta-edit-icon"
259
+ onClick={() => isSavedCta(index, false)}
260
+ >
261
+ <CapIcon size="s" type="edit" className="cta-action" />
262
+ </CapColumn>
263
+ <CapColumn onClick={() => deleteHandler(index)} className="cta-del-icon">
264
+ <CapIcon size="s" type="delete" className="cta-action" ariaLabel="delete-cta-icon" />
265
+ </CapColumn>
266
+ </CapRow>
267
+ )}
268
+ </CapRow>
269
+ );
270
+ } else {
271
+ renderArray.push(
272
+ <CapRow
273
+ className="cap-inapp-cta"
274
+ id={`cap-inapp-cta-${index}`}
275
+ >
276
+ <CapColumn>
277
+ <CapHeading type="h4" className="cta-label">
278
+ {buttonType === PRIMARY ? formatMessage(messages.ctaButtonText) : formatMessage(messages.ctaButtonTextSecondary)}
279
+ </CapHeading>
280
+ <CapInput
281
+ id={index}
282
+ onChange={onButtonTextChange}
283
+ placeholder={formatMessage(messages.ctaButtonTextPlaceholder)}
284
+ value={text}
285
+ maxLength={BTN_MAX_LENGTH}
286
+ errorMessage={buttonError}
287
+ />
288
+ {renderLength(text.length, BTN_MAX_LENGTH)}
289
+ </CapColumn>
290
+ <CapColumn className="inapp-cta-buttons">
291
+ <CapRow style={{ width: '30%', marginRight: '10px' }}>
292
+ <CapHeading type="h4" className="cta-label">
293
+ {formatMessage(messages.ctaType)}
294
+ </CapHeading>
295
+ <CapSelect.CapCustomSelect
296
+ id="inapp-cta-type"
297
+ key="mpush-cta-type"
298
+ options={CTA_OPTIONS || []}
299
+ onChange={(value) => onUrlTypeChange(value, index)}
300
+ value={urlType}
301
+ />
302
+ </CapRow>
303
+ {urlType === DEEP_LINK && (
304
+ <CapRow style={{ width: '70%' }}>
305
+ <CapHeading type="h4">
306
+ {formatMessage(messages.urlDeepLink)}
307
+ </CapHeading>
308
+ <CapSelect.CapCustomSelect
309
+ id="inapp-deep-link-type"
310
+ key="mpush-deep-link-type"
311
+ placeholder={formatMessage(messages.ctaDeepLinkOptionsPlaceholder)}
312
+ options={deepLink || []}
313
+ onChange={(value) => onDeepLinkSelect(value, index)}
314
+ value={ctaDeepLinkValue}
315
+ />
316
+ </CapRow>
317
+ )}
318
+ {urlType === EXTERNAL_URL && (
319
+ <CapRow style={{ width: '70%' }}>
320
+ <CapHeading type="h4">
321
+ {formatMessage(messages.urlExternalLink)}
322
+ </CapHeading>
323
+ <CapInput
324
+ id="inapp-cta-external-link"
325
+ className="inapp-cta-external-link"
326
+ onChange={onUrlChange}
327
+ placeholder={formatMessage(messages.ctaExternalLinkPlaceholder)}
328
+ value={localUrlValues[index] || ''}
329
+ size="large"
330
+ maxLength={URL_MAX_LENGTH}
331
+ errorMessage={urlError}
332
+ data-index={index}
333
+ />
334
+ </CapRow>
335
+ )}
336
+ </CapColumn>
337
+ {urlType === DEEP_LINK && ctaDeepLinkValue && deepLinkKeysFromSelectionArray.length > 0 && (
338
+ <CapRow style={{ marginTop: '10px' }}>
339
+ <CapHeading type="h4">
340
+ {formatMessage(messages.deepLinkKeys)}
341
+ </CapHeading>
342
+ <CapLabel type="label2" className="cta-deep-link-keys-value">
343
+ {(() => {
344
+ if (deepLinkKeysFromSelectionArray.length > 0) {
345
+ return deepLinkKeysFromSelectionArray.join(', ');
346
+ }
347
+ if (deepLinkKeysArray.length > 0) {
348
+ return deepLinkKeysArray.join(', ');
349
+ }
350
+ return "No value set";
351
+ })()}
352
+ </CapLabel>
353
+ <CapInput
354
+ id="inapp-deep-link-keys"
355
+ className="inapp-deep-link-keys"
356
+ onChange={onDeepLinkKeysChange}
357
+ placeholder={formatMessage(messages.deepLinkKeysPlaceholder, { key: deepLinkKeysFromSelectionArray.join(', ') || 'deep link keys' })}
358
+ value={Array.isArray(ctaDeepLinkKeysValue) ? ctaDeepLinkKeysValue.join(', ') : (ctaDeepLinkKeysValue || '')}
359
+ size="large"
360
+ data-index={index}
361
+ errorMessage={deepLinkKeysError ? formatMessage(messages.deepLinkKeysRequired) : ""}
362
+ />
363
+ </CapRow>
364
+ )}
365
+ <CapRow className="inapp-cta-save-delete-btn">
366
+ <CapTooltip
367
+ title={
368
+ ctaSaveDisabled(index)
369
+ ? formatMessage(messages.ctaSaveDisabled)
370
+ : ""
371
+ }
372
+ placement={"bottom"}
373
+ >
374
+ <div className="button-disabled-tooltip-wrapper">
375
+ <CapButton
376
+ onClick={() => isSavedCta(index, true)}
377
+ disabled={ctaSaveDisabled(index)}
378
+ className="inapp-cta-save-btn"
379
+ >
380
+ {formatMessage(globalMessages.save)}
381
+ </CapButton>
382
+ </div>
383
+ </CapTooltip>
384
+ <CapButton
385
+ onClick={() => deleteHandler(index)}
386
+ className="inapp-cta-delete-btn"
387
+ type="secondary"
388
+ >
389
+ {formatMessage(globalMessages.delete)}
390
+ </CapButton>
391
+ </CapRow>
392
+ </CapRow>
393
+ );
394
+ }
395
+ });
396
+ return renderArray;
397
+ };
398
+
399
+ return renderedContent();
400
+ };
401
+
402
+ export default injectIntl(CapMpushCTA);