@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.
Files changed (110) hide show
  1. package/config/app.js +6 -0
  2. package/containers/App/constants.js +1 -0
  3. package/index.html +3 -1
  4. package/initialReducer.js +2 -0
  5. package/package.json +1 -1
  6. package/services/api.js +94 -1
  7. package/services/tests/api.test.js +191 -0
  8. package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +8 -3
  9. package/tests/integration/TemplateCreation/api-response.js +5 -0
  10. package/tests/integration/TemplateCreation/msw-handler.js +42 -63
  11. package/utils/common.js +7 -0
  12. package/utils/commonUtils.js +2 -6
  13. package/utils/createPayload.js +272 -0
  14. package/utils/tests/createPayload.test.js +761 -0
  15. package/v2Components/CapImageUpload/index.js +59 -46
  16. package/v2Components/CapInAppCTA/index.js +1 -0
  17. package/v2Components/CapMpushCTA/constants.js +25 -0
  18. package/v2Components/CapMpushCTA/index.js +332 -0
  19. package/v2Components/CapMpushCTA/index.scss +95 -0
  20. package/v2Components/CapMpushCTA/messages.js +89 -0
  21. package/v2Components/CapTagList/index.js +177 -120
  22. package/v2Components/CapVideoUpload/constants.js +3 -0
  23. package/v2Components/CapVideoUpload/index.js +167 -110
  24. package/v2Components/CapVideoUpload/messages.js +16 -0
  25. package/v2Components/Carousel/index.js +15 -13
  26. package/v2Components/CustomerSearchSection/_customerSearch.scss +309 -0
  27. package/v2Components/CustomerSearchSection/constants.js +5 -0
  28. package/v2Components/CustomerSearchSection/index.js +367 -0
  29. package/v2Components/CustomerSearchSection/messages.js +20 -0
  30. package/v2Components/CustomerSearchSection/tests/utils.test.js +334 -0
  31. package/v2Components/CustomerSearchSection/utils.js +49 -0
  32. package/v2Components/ErrorInfoNote/style.scss +1 -0
  33. package/v2Components/MobilePushPreviewV2/index.js +37 -5
  34. package/v2Components/TemplatePreview/_templatePreview.scss +114 -72
  35. package/v2Components/TemplatePreview/assets/images/Android _ With date and time.svg +29 -0
  36. package/v2Components/TemplatePreview/assets/images/android.svg +9 -0
  37. package/v2Components/TemplatePreview/assets/images/iOS _ With date and time.svg +26 -0
  38. package/v2Components/TemplatePreview/assets/images/ios.svg +9 -0
  39. package/v2Components/TemplatePreview/index.js +178 -50
  40. package/v2Components/TemplatePreview/messages.js +4 -0
  41. package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +169 -0
  42. package/v2Components/TestAndPreviewSlidebox/LeftPanelContent.js +95 -0
  43. package/v2Components/TestAndPreviewSlidebox/PreviewSection.js +69 -0
  44. package/v2Components/TestAndPreviewSlidebox/SendTestMessage.js +68 -0
  45. package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +543 -0
  46. package/v2Components/TestAndPreviewSlidebox/actions.js +67 -0
  47. package/v2Components/TestAndPreviewSlidebox/constants.js +67 -0
  48. package/v2Components/TestAndPreviewSlidebox/index.js +592 -0
  49. package/v2Components/TestAndPreviewSlidebox/messages.js +147 -0
  50. package/v2Components/TestAndPreviewSlidebox/reducer.js +233 -0
  51. package/v2Components/TestAndPreviewSlidebox/sagas.js +258 -0
  52. package/v2Components/TestAndPreviewSlidebox/selectors.js +142 -0
  53. package/v2Components/TestAndPreviewSlidebox/tests/CustomValuesEditor.test.js +425 -0
  54. package/v2Components/TestAndPreviewSlidebox/tests/LeftPanelContent.test.js +400 -0
  55. package/v2Components/TestAndPreviewSlidebox/tests/SendTestMessage.test.js +448 -0
  56. package/v2Components/TestAndPreviewSlidebox/tests/actions.test.js +80 -0
  57. package/v2Components/TestAndPreviewSlidebox/tests/reducer.test.js +367 -0
  58. package/v2Components/TestAndPreviewSlidebox/tests/saga.rtl.test.js +192 -0
  59. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +652 -0
  60. package/v2Components/TestAndPreviewSlidebox/tests/selector.test.js +182 -0
  61. package/v2Containers/CreativesContainer/SlideBoxContent.js +22 -10
  62. package/v2Containers/CreativesContainer/SlideBoxFooter.js +23 -2
  63. package/v2Containers/CreativesContainer/index.js +216 -136
  64. package/v2Containers/CreativesContainer/messages.js +4 -0
  65. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +21 -0
  66. package/v2Containers/Email/index.js +27 -2
  67. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +10 -0
  68. package/v2Containers/EmailWrapper/index.js +6 -0
  69. package/v2Containers/InApp/constants.js +1 -0
  70. package/v2Containers/InApp/index.js +13 -13
  71. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -0
  72. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -0
  73. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -0
  74. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -0
  75. package/v2Containers/MobilePush/Create/index.js +1 -0
  76. package/v2Containers/MobilePush/commonMethods.js +7 -14
  77. package/v2Containers/MobilePushNew/actions.js +116 -0
  78. package/v2Containers/MobilePushNew/components/CtaButtons.js +170 -0
  79. package/v2Containers/MobilePushNew/components/MediaUploaders.js +754 -0
  80. package/v2Containers/MobilePushNew/components/PlatformContentFields.js +279 -0
  81. package/v2Containers/MobilePushNew/components/index.js +5 -0
  82. package/v2Containers/MobilePushNew/components/tests/CtaButtons.test.js +779 -0
  83. package/v2Containers/MobilePushNew/components/tests/MediaUploaders.test.js +2114 -0
  84. package/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +343 -0
  85. package/v2Containers/MobilePushNew/constants.js +115 -0
  86. package/v2Containers/MobilePushNew/hooks/tests/usePlatformSync.test.js +1299 -0
  87. package/v2Containers/MobilePushNew/hooks/tests/useUpload.test.js +1223 -0
  88. package/v2Containers/MobilePushNew/hooks/usePlatformSync.js +246 -0
  89. package/v2Containers/MobilePushNew/hooks/useUpload.js +726 -0
  90. package/v2Containers/MobilePushNew/index.js +2280 -0
  91. package/v2Containers/MobilePushNew/index.scss +308 -0
  92. package/v2Containers/MobilePushNew/messages.js +226 -0
  93. package/v2Containers/MobilePushNew/reducer.js +160 -0
  94. package/v2Containers/MobilePushNew/sagas.js +198 -0
  95. package/v2Containers/MobilePushNew/selectors.js +55 -0
  96. package/v2Containers/MobilePushNew/tests/reducer.test.js +741 -0
  97. package/v2Containers/MobilePushNew/tests/sagas.test.js +863 -0
  98. package/v2Containers/MobilePushNew/tests/selectors.test.js +425 -0
  99. package/v2Containers/MobilePushNew/tests/utils.test.js +322 -0
  100. package/v2Containers/MobilePushNew/utils.js +33 -0
  101. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +23 -5
  102. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -0
  103. package/v2Containers/TagList/index.js +56 -10
  104. package/v2Containers/Templates/_templates.scss +101 -1
  105. package/v2Containers/Templates/index.js +147 -35
  106. package/v2Containers/Templates/messages.js +8 -0
  107. package/v2Containers/Templates/sagas.js +2 -0
  108. package/v2Containers/Whatsapp/constants.js +1 -0
  109. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +35 -0
  110. 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 { 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,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
+ }