@capillarytech/creatives-library 9.0.26 → 9.0.29-alpha.1

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 (149) hide show
  1. package/constants/unified.js +29 -0
  2. package/global-styles.js +1 -1
  3. package/package.json +1 -1
  4. package/services/tests/api.test.js +35 -20
  5. package/utils/commonUtils.js +22 -1
  6. package/utils/rcsPayloadUtils.js +102 -0
  7. package/utils/templateVarUtils.js +198 -0
  8. package/utils/tests/rcsPayloadUtils.test.js +295 -0
  9. package/utils/tests/templateVarUtils.test.js +204 -0
  10. package/v2Components/CapActionButton/constants.js +21 -1
  11. package/v2Components/CapActionButton/index.js +260 -144
  12. package/v2Components/CapActionButton/index.scss +245 -14
  13. package/v2Components/CapActionButton/messages.js +32 -3
  14. package/v2Components/CapActionButton/tests/index.test.js +74 -19
  15. package/v2Components/CapImageUpload/index.js +2 -2
  16. package/v2Components/CapTagList/index.js +9 -0
  17. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -48
  18. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
  19. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +214 -21
  20. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
  21. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +83 -9
  22. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
  23. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +58 -11
  24. package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
  25. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +186 -22
  26. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +381 -80
  27. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +155 -8
  28. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
  29. package/v2Components/CommonTestAndPreview/constants.js +35 -2
  30. package/v2Components/CommonTestAndPreview/index.js +770 -231
  31. package/v2Components/CommonTestAndPreview/messages.js +45 -3
  32. package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
  33. package/v2Components/CommonTestAndPreview/sagas.js +25 -6
  34. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +1 -0
  35. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +412 -257
  36. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +337 -63
  37. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
  38. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
  39. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
  40. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +108 -15
  41. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
  42. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
  43. package/v2Components/CommonTestAndPreview/tests/index.test.js +133 -4
  44. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
  45. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +31 -24
  46. package/v2Components/CommonTestAndPreview/tests/utils.test.js +151 -0
  47. package/v2Components/CommonTestAndPreview/utils.js +84 -0
  48. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +91 -0
  49. package/v2Components/SmsFallback/constants.js +94 -0
  50. package/v2Components/SmsFallback/index.js +958 -0
  51. package/v2Components/SmsFallback/index.scss +266 -0
  52. package/v2Components/SmsFallback/messages.js +78 -0
  53. package/v2Components/SmsFallback/smsFallbackUtils.js +120 -0
  54. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
  55. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
  56. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
  57. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +208 -0
  58. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +309 -0
  59. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +471 -0
  60. package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
  61. package/v2Components/TemplatePreview/_templatePreview.scss +45 -25
  62. package/v2Components/TemplatePreview/constants.js +2 -0
  63. package/v2Components/TemplatePreview/index.js +147 -32
  64. package/v2Components/TemplatePreview/tests/index.test.js +142 -0
  65. package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +6 -6
  66. package/v2Components/TestAndPreviewSlidebox/index.js +13 -1
  67. package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
  68. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
  69. package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
  70. package/v2Components/VarSegmentMessageEditor/index.js +125 -0
  71. package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
  72. package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +4 -1
  73. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +4 -0
  74. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +20 -20
  75. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +27 -0
  76. package/v2Containers/CreativesContainer/SlideBoxContent.js +37 -4
  77. package/v2Containers/CreativesContainer/SlideBoxFooter.js +14 -5
  78. package/v2Containers/CreativesContainer/SlideBoxHeader.js +36 -5
  79. package/v2Containers/CreativesContainer/constants.js +11 -0
  80. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +77 -0
  81. package/v2Containers/CreativesContainer/index.js +328 -106
  82. package/v2Containers/CreativesContainer/index.scss +102 -1
  83. package/v2Containers/CreativesContainer/tests/CreativesSlideBoxWrapper.test.js +58 -0
  84. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
  85. package/v2Containers/CreativesContainer/tests/SlideBoxContent.smsDltPreview.test.js +73 -0
  86. package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +37 -0
  87. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +103 -34
  88. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +193 -15
  89. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +88 -0
  90. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +549 -57
  91. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
  92. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
  93. package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
  94. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
  95. package/v2Containers/Email/index.js +3 -54
  96. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +4 -9
  97. package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
  98. package/v2Containers/Rcs/carouselUtils.js +224 -0
  99. package/v2Containers/Rcs/components/CarouselCard.js +317 -0
  100. package/v2Containers/Rcs/components/CarouselCardButtons.js +113 -0
  101. package/v2Containers/Rcs/components/CarouselCardMedia.js +136 -0
  102. package/v2Containers/Rcs/components/CarouselCharacterCount.js +31 -0
  103. package/v2Containers/Rcs/components/CarouselDimensionSelection.js +80 -0
  104. package/v2Containers/Rcs/constants.js +132 -16
  105. package/v2Containers/Rcs/index.js +1808 -949
  106. package/v2Containers/Rcs/index.scss +443 -8
  107. package/v2Containers/Rcs/messages.js +45 -22
  108. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +484 -0
  109. package/v2Containers/Rcs/tests/CarouselCard.test.js +464 -0
  110. package/v2Containers/Rcs/tests/CarouselCardButtons.test.js +211 -0
  111. package/v2Containers/Rcs/tests/CarouselCardMedia.test.js +160 -0
  112. package/v2Containers/Rcs/tests/CarouselCharacterCount.test.js +50 -0
  113. package/v2Containers/Rcs/tests/CarouselDimensionSelection.test.js +119 -0
  114. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +74176 -39409
  115. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +10 -74
  116. package/v2Containers/Rcs/tests/carouselUtils.test.js +916 -0
  117. package/v2Containers/Rcs/tests/index.test.js +219 -40
  118. package/v2Containers/Rcs/tests/mockData.js +38 -0
  119. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +873 -0
  120. package/v2Containers/Rcs/tests/utils.test.js +682 -30
  121. package/v2Containers/Rcs/utils.js +514 -12
  122. package/v2Containers/Sms/Create/index.js +115 -48
  123. package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
  124. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  125. package/v2Containers/SmsTrai/Create/index.js +9 -4
  126. package/v2Containers/SmsTrai/Edit/constants.js +2 -0
  127. package/v2Containers/SmsTrai/Edit/index.js +678 -169
  128. package/v2Containers/SmsTrai/Edit/index.scss +126 -0
  129. package/v2Containers/SmsTrai/Edit/messages.js +14 -4
  130. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +5615 -3014
  131. package/v2Containers/SmsWrapper/index.js +37 -8
  132. package/v2Containers/TagList/index.js +5 -0
  133. package/v2Containers/Templates/TemplatesActionBar.js +101 -0
  134. package/v2Containers/Templates/_templates.scss +196 -12
  135. package/v2Containers/Templates/actions.js +11 -0
  136. package/v2Containers/Templates/constants.js +2 -0
  137. package/v2Containers/Templates/index.js +131 -59
  138. package/v2Containers/Templates/sagas.js +57 -13
  139. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +129 -0
  140. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1060 -1015
  141. package/v2Containers/Templates/tests/sagas.test.js +199 -16
  142. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
  143. package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
  144. package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
  145. package/v2Containers/TemplatesV2/index.js +88 -25
  146. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
  147. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
  148. package/v2Containers/Whatsapp/index.js +3 -20
  149. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
@@ -8,6 +8,7 @@ import CapRow from '@capillarytech/cap-ui-library/CapRow';
8
8
  import CapColumn from '@capillarytech/cap-ui-library/CapColumn';
9
9
  import CapHeading from '@capillarytech/cap-ui-library/CapHeading';
10
10
  import CapHeader from "@capillarytech/cap-ui-library/CapHeader";
11
+ import CapRadioGroup from '@capillarytech/cap-ui-library/CapRadioGroup';
11
12
  import CapSelect from '@capillarytech/cap-ui-library/CapSelect';
12
13
  import CapInput from '@capillarytech/cap-ui-library/CapInput';
13
14
  import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
@@ -16,13 +17,24 @@ import CapButton from '@capillarytech/cap-ui-library/CapButton';
16
17
  import CapTooltip from '@capillarytech/cap-ui-library/CapTooltip';
17
18
  import CapTooltipWithInfo from '@capillarytech/cap-ui-library/CapTooltipWithInfo';
18
19
  import { CAP_SPACE_04 } from '@capillarytech/cap-ui-library/styled/variables';
19
- import globalMessages from '../../v2Containers/Cap/messages';import CapTagListWithInput from '../CapTagListWithInput';
20
+ import globalMessages from '../../v2Containers/Cap/messages';
21
+ import TagList from '../../v2Containers/TagList';
20
22
 
21
23
  import { isUrl, isValidText } from '../../v2Containers/Line/Container/Wrapper/utils';
22
24
  import messages from './messages';
23
- import { CTA_OPTIONS, BTN_MAX_LENGTH, URL_MAX_LENGTH, PHONE_NUMBER_MAX_LENGTH,invalidVarRegex, HANDLERS } from './constants';
25
+ import {
26
+ BTN_MAX_LENGTH,
27
+ URL_MAX_LENGTH,
28
+ PHONE_NUMBER_MAX_LENGTH,
29
+ invalidVarRegex,
30
+ HANDLERS,
31
+ RCS_CTA_URL_TYPE,
32
+ CTA_TYPE_RADIO_OPTIONS,
33
+ CTA_URL_TYPE_SELECT_OPTIONS,
34
+ DYNAMIC_URL_SUFFIX,
35
+ } from './constants';
24
36
  import './index.scss';
25
- import { INITIAL_SUGGESTIONS, RCS_BUTTON_TYPES} from '../../v2Containers/Rcs/constants';
37
+ import { INITIAL_SUGGESTIONS, RCS_BUTTON_TYPES, HOST_ICS} from '../../v2Containers/Rcs/constants';
26
38
 
27
39
  const { TextArea } = CapInput;
28
40
 
@@ -41,9 +53,13 @@ export const CapActionButton = (props) => {
41
53
  injectedTags = {},
42
54
  location = {},
43
55
  selectedOfferDetails = [],
44
-
56
+ host = '',
45
57
  onContextChange,
58
+ minSavedSuggestions = 0,
59
+ hideDeleteSuggestionIndexes = [],
46
60
  } = props;
61
+ const { formatMessage } = intl;
62
+ const isHostIcs = host === HOST_ICS;
47
63
  const [urlError, setUrlError] = useState(false);
48
64
  const [buttonError, setButtonError] = useState(false);
49
65
  const updateHandler = (type, value, index) => {
@@ -66,6 +82,7 @@ export const CapActionButton = (props) => {
66
82
 
67
83
  const onCtaTypeChange = (value, index) => {
68
84
  let clonedCta = cloneDeep(suggestions[index]);
85
+ const isCta = value === RCS_BUTTON_TYPES.CTA;
69
86
  clonedCta = {
70
87
  ...clonedCta,
71
88
  type: value,
@@ -73,6 +90,7 @@ export const CapActionButton = (props) => {
73
90
  phoneNumber: '',
74
91
  postback: '',
75
92
  url: value === RCS_BUTTON_TYPES.PHONE_NUMBER ? null : '',
93
+ urlType: isCta ? RCS_CTA_URL_TYPE.STATIC : undefined,
76
94
  isSaved: false,
77
95
  };
78
96
  setUrlError(false);
@@ -84,7 +102,7 @@ export const CapActionButton = (props) => {
84
102
  const { value, id } = target;
85
103
  let errorMessage = '';
86
104
  if (value.length > BTN_MAX_LENGTH) {
87
- errorMessage = formatMessage(messages.ctaButtonTextLengthError);
105
+ errorMessage = formatMessage(messages.ctaButtonTextLengthError, { maxLength: BTN_MAX_LENGTH });
88
106
  } if (!isValidText(value)) {
89
107
  errorMessage = formatMessage(messages.ctaButtonErrorMessage);
90
108
  }
@@ -92,44 +110,91 @@ export const CapActionButton = (props) => {
92
110
  updateDisplayAndPostback(value, id);
93
111
  };
94
112
 
95
- const renderLength = (len, max) => (
96
- <CapHeading type="label1" className="rcs-render-btn-length">
97
- {formatMessage(messages.templateMessageLength, {
98
- currentLength: len,
99
- maxLength: max,
100
- })}
113
+ const renderInnerCharCount = (len, max) => (
114
+ <CapHeading type="h5" className="rcs-cta-inner-char-count">
115
+ {formatMessage(messages.ctaFieldCharCountInline, { current: len, max })}
101
116
  </CapHeading>
102
117
  );
103
118
 
104
- const onUrlChange = ({ target }) => {
105
- let { value, id } = target;
106
- let errorMessage = false;
107
- if (!isUrl(value)) {
108
- errorMessage = formatMessage(messages.ctaWebsiteUrlErrorMessage);
109
- } else if (value.match(invalidVarRegex)?.length > 0) {
110
- errorMessage = formatMessage(messages.staticUrlWithVarErrorMessage);
119
+ const validateCtaUrlValue = (value, urlSubtype) => {
120
+ const trimmedUrl = String(value || '').trim();
121
+ if (!trimmedUrl) {
122
+ return formatMessage(messages.ctaWebsiteUrlErrorMessage);
123
+ }
124
+ if (trimmedUrl.length > URL_MAX_LENGTH) {
125
+ return formatMessage(messages.ctaWebsiteUrlErrorMessage);
126
+ }
127
+ if (urlSubtype === RCS_CTA_URL_TYPE.DYNAMIC) {
128
+ const baseUrl = trimmedUrl.replace(invalidVarRegex, '');
129
+ return isUrl(baseUrl) ? false : formatMessage(messages.ctaWebsiteUrlErrorMessage);
130
+ }
131
+ if (!isUrl(trimmedUrl)) {
132
+ return formatMessage(messages.ctaWebsiteUrlErrorMessage);
111
133
  }
112
- setUrlError(errorMessage);
134
+ if (trimmedUrl.match(invalidVarRegex)?.length > 0) {
135
+ return formatMessage(messages.staticUrlWithVarErrorMessage);
136
+ }
137
+ return false;
138
+ };
139
+
140
+ const onUrlChange = ({ target }) => {
141
+ const { id } = target;
142
+ const row = suggestions[id] || {};
143
+ const subtype = row.urlType || RCS_CTA_URL_TYPE.STATIC;
144
+ const value = subtype === RCS_CTA_URL_TYPE.DYNAMIC
145
+ ? target.value.replace(invalidVarRegex, '')
146
+ : target.value;
147
+ setUrlError(validateCtaUrlValue(value, subtype));
113
148
  updateHandler(HANDLERS.URL, value, id);
114
149
  };
115
150
 
151
+ const onUrlSubtypeChange = (nextSubtype, index) => {
152
+ const cloned = cloneDeep(suggestions[index]);
153
+ cloned.urlType = nextSubtype;
154
+ cloned.isSaved = false;
155
+ setUrlError(validateCtaUrlValue(cloned.url || '', nextSubtype));
156
+ updateButtonChange(cloned, index);
157
+ };
158
+
159
+ const onTagSelect = (data, index, url) => {
160
+ updateHandler(HANDLERS.URL, (url || '').replace(DYNAMIC_URL_SUFFIX, `{{${data}}}`), index);
161
+ };
162
+
163
+ const revertTagSelect = (index, url) => {
164
+ updateHandler(HANDLERS.URL, (url || '').replace(invalidVarRegex, DYNAMIC_URL_SUFFIX), index);
165
+ };
166
+
116
167
  const onPhoneNoChange = (value, index) => {
117
168
  updateHandler(HANDLERS.PHONE_NUMBER, value, index);
118
169
  };
119
170
 
120
171
  const ctaSaveDisabled = (index) => {
121
- const { type, text, phoneNumber, url } = suggestions[index] || {};
172
+ const { type, text, phoneNumber, url, urlType } = suggestions[index] || {};
122
173
  if (text === '' || buttonError) {
123
174
  return true;
124
- } if (type === RCS_BUTTON_TYPES.PHONE_NUMBER && phoneNumber.length < 5) {
125
- return true;
126
- } if (type === RCS_BUTTON_TYPES.CTA && (url === '' || urlError)) {
175
+ } if (type === RCS_BUTTON_TYPES.PHONE_NUMBER && (phoneNumber || '').length < 5) {
127
176
  return true;
177
+ } if (type === RCS_BUTTON_TYPES.CTA) {
178
+ const subtype = urlType || RCS_CTA_URL_TYPE.STATIC;
179
+ if (validateCtaUrlValue(url, subtype)) {
180
+ return true;
181
+ }
182
+ // A dynamic URL still holding the unresolved {{1}} placeholder has no real
183
+ // personalization tag assigned yet — block save until one is picked via "Add URL label".
184
+ return subtype === RCS_CTA_URL_TYPE.DYNAMIC && (url || '').includes(DYNAMIC_URL_SUFFIX);
128
185
  }
129
186
  return false;
130
187
  };
131
188
 
132
189
  const saveCta = (index) => {
190
+ const cta = suggestions[index];
191
+ if (
192
+ cta?.type === RCS_BUTTON_TYPES.CTA
193
+ && cta?.urlType === RCS_CTA_URL_TYPE.DYNAMIC
194
+ && !(cta?.url || '').includes(DYNAMIC_URL_SUFFIX)
195
+ ) {
196
+ cta.url = `${cta.url || ''}${DYNAMIC_URL_SUFFIX}`;
197
+ }
133
198
  updateHandler(HANDLERS.IS_SAVED, true, index);
134
199
  };
135
200
 
@@ -143,64 +208,46 @@ export const CapActionButton = (props) => {
143
208
  updateButtonChange(newSuggestion, suggestions?.length);
144
209
  };
145
210
 
146
- const renderCtaOptions = (label, tooltipLabel, isDisabled) => {
147
- if (isDisabled) {
148
- return (
149
- <CapRow>
150
- <CapColumn span={23}>{label}</CapColumn>
151
- <CapColumn span={1}>
152
- <CapTooltipWithInfo
153
- // autoAdjustOverflow
154
- placement="right"
155
- title={tooltipLabel}
156
- />
157
- </CapColumn>
158
- </CapRow>
159
- );
160
- }
161
- return label;
162
- };
211
+ const hideDeleteForSuggestionIndex = (idx) =>
212
+ Array.isArray(hideDeleteSuggestionIndexes) && hideDeleteSuggestionIndexes.includes(idx);
163
213
 
164
- const { formatMessage } = intl;
165
214
  const renderedContent = () => {
166
215
  const renderArray = [];
167
- const addBtnDisabled = suggestions.length >= maxButtons || !suggestions[suggestions.length - 1]?.isSaved;
168
-
169
- const ctaOptions = CTA_OPTIONS.map((option) => {
170
- const { value, label } = option;
171
- const isDisabled = suggestions.length >= maxButtons && !suggestions[suggestions.length - 1];
172
- return {
173
- value,
174
- label: renderCtaOptions(label, formatMessage(messages.ctaButtonTypeDisabled), isDisabled),
175
- disabled: isDisabled,
176
- };
177
- });
216
+ const addBtnDisabled = suggestions?.length > 0 && (suggestions.length >= maxButtons || !suggestions[suggestions.length - 1]?.isSaved);
217
+ const savedSuggestionsCount = (suggestions || []).filter((s) => s && s.isSaved).length;
218
+ const cannotDeleteSavedMandatory = (idx) => {
219
+ const row = suggestions[idx];
220
+ if (!row || !row.isSaved) return false;
221
+ return minSavedSuggestions > 0 && savedSuggestionsCount <= minSavedSuggestions;
222
+ };
178
223
 
179
- suggestions.forEach((cta) => {
224
+ suggestions?.length > 0 && suggestions.forEach((cta) => {
180
225
  const {
181
226
  index, type, text, isSaved,
182
227
  } = cta || {};
183
228
 
184
229
  const url = type !== RCS_BUTTON_TYPES.CTA ? null : cta.url;
230
+ const urlSubtype = type === RCS_BUTTON_TYPES.CTA
231
+ ? (cta.urlType || RCS_CTA_URL_TYPE.STATIC)
232
+ : RCS_CTA_URL_TYPE.STATIC;
185
233
  const phoneNumber = type !== RCS_BUTTON_TYPES.PHONE_NUMBER ? null : cta.phoneNumber;
186
234
  if (isFullMode && !isEditFlow && !isSaved) {
187
235
  renderArray.push(
188
- <div className="rcs-button-cta-create-container" key={`cta-edit-${index}`}>
189
- <CapRow type='flex' justify='space-between' className="rcs-button-cta-create margin-t-16">
190
- <CapColumn span={11}>
191
- {/* Type of action */}
192
- <CapHeading type="h4" className="cta-label">
236
+ <CapRow className="rcs-button-cta-create-container" key={`cta-edit-${index}`} gutter={0}>
237
+ <CapRow className="rcs-button-cta-create" gutter={0}>
238
+ <CapColumn span={24}>
239
+ <CapHeading type="h4">
193
240
  {formatMessage(messages.ctaType)}
194
241
  </CapHeading>
195
- <CapSelect
196
- id="rcs-cta-type"
197
- options={ctaOptions || []}
198
- onChange={(value) => onCtaTypeChange(value, index)}
242
+ <CapRadioGroup
243
+ id={`rcs-cta-type-radio-${index}`}
244
+ className="cap-rcs-cta-type-radio"
245
+ options={CTA_TYPE_RADIO_OPTIONS}
199
246
  value={type}
200
- disabled={false}
247
+ onChange={(e) => onCtaTypeChange(e.target.value, index)}
201
248
  />
202
249
  </CapColumn>
203
- <CapColumn span={12}>
250
+ <CapColumn span={24}>
204
251
  <CapHeading type="h4" className="cta-label">
205
252
  {formatMessage(messages.ctaButtonText)}
206
253
  <CapTooltipWithInfo
@@ -221,28 +268,34 @@ export const CapActionButton = (props) => {
221
268
  size="large"
222
269
  maxLength={BTN_MAX_LENGTH}
223
270
  errorMessage={buttonError}
271
+ suffix={renderInnerCharCount((text || '').length, BTN_MAX_LENGTH)}
224
272
  />
225
- {renderLength(text.length, BTN_MAX_LENGTH)}
226
273
  </CapColumn>
227
274
  </CapRow>
228
275
  {type === RCS_BUTTON_TYPES.PHONE_NUMBER && (
229
276
  <>
230
277
  <CapRow>
231
- <CapColumn span={11}>
232
- {/* phone number */}
278
+ <CapColumn span={24}>
233
279
  <CapHeading type="h4" className="cta-label">
234
280
  {formatMessage(messages.ctaPhoneNo)}
235
281
  </CapHeading>
236
- <PhoneInput
237
- placeholder={formatMessage(messages.ctaPhoneNoPlaceholder)}
238
- autoFormat={false}
239
- countryCodeEditable={false}
240
- value={phoneNumber}
241
- onChange={(value) => onPhoneNoChange(value, index)}
242
- country="in"
243
- className="cta-phone-number"
244
- />
245
- {renderLength(phoneNumber.length, PHONE_NUMBER_MAX_LENGTH)}
282
+ <CapRow
283
+ className="rcs-cta-input-with-inner-count rcs-cta-input-with-inner-count--phone"
284
+ gutter={0}
285
+ >
286
+ <CapColumn span={24}>
287
+ <PhoneInput
288
+ placeholder={formatMessage(messages.ctaPhoneNoPlaceholder)}
289
+ autoFormat={false}
290
+ countryCodeEditable={false}
291
+ value={phoneNumber}
292
+ onChange={(value) => onPhoneNoChange(value, index)}
293
+ country="in"
294
+ className="cta-phone-number rcs-cta-phone-input"
295
+ />
296
+ {renderInnerCharCount((phoneNumber || '').length, PHONE_NUMBER_MAX_LENGTH)}
297
+ </CapColumn>
298
+ </CapRow>
246
299
  </CapColumn>
247
300
  </CapRow>
248
301
  <CapRow useLegacy className="rcs-cta-save-delete-btn">
@@ -250,7 +303,7 @@ export const CapActionButton = (props) => {
250
303
  title={ctaSaveDisabled(index) && formatMessage(messages.ctaSaveDisabled)}
251
304
  placement="bottom"
252
305
  >
253
- <div className="button-disabled-tooltip-wrapper">
306
+ <CapRow className="button-disabled-tooltip-wrapper">
254
307
  <CapButton
255
308
  onClick={() => saveCta(index)}
256
309
  disabled={ctaSaveDisabled(index)}
@@ -258,36 +311,68 @@ export const CapActionButton = (props) => {
258
311
  >
259
312
  {formatMessage(globalMessages.save)}
260
313
  </CapButton>
261
- </div>
314
+ </CapRow>
262
315
  </CapTooltip>
263
- <CapButton
264
- onClick={() => deleteButtonHandler(index)}
265
- className="rcs-cta-delete-btn"
266
- type="secondary"
267
- >
268
- {formatMessage(globalMessages.delete)}
269
- </CapButton>
316
+ {!hideDeleteForSuggestionIndex(index) && !cannotDeleteSavedMandatory(index) && (
317
+ <CapButton
318
+ onClick={() => deleteButtonHandler(index)}
319
+ className="rcs-cta-delete-btn"
320
+ type="secondary"
321
+ >
322
+ {formatMessage(globalMessages.delete)}
323
+ </CapButton>
324
+ )}
270
325
  </CapRow>
271
326
  </>
272
327
  )}
273
328
  {type === RCS_BUTTON_TYPES.CTA && (
274
329
  <>
275
- <CapRow>
276
- <CapColumn span={24}>
330
+ <CapRow className="rcs-cta-url-fields-row" gutter={8}>
331
+ <CapColumn span={6} className="rcs-cta-url-type-col">
332
+ <CapHeading type="h4" className="cta-label">
333
+ {formatMessage(messages.ctaWebsiteType)}
334
+ </CapHeading>
335
+ <CapSelect
336
+ id={`rcs-cta-url-type-${index}`}
337
+ options={CTA_URL_TYPE_SELECT_OPTIONS}
338
+ value={urlSubtype}
339
+ onChange={(value) => onUrlSubtypeChange(value, index)}
340
+ />
341
+ </CapColumn>
342
+ <CapColumn span={18} className="rcs-cta-url-value-col">
277
343
  <CapHeading type="h4" className="cta-label">
278
- {formatMessage(messages.ctaWebsite)}
344
+ {formatMessage(messages.ctaWebsiteUrl)}
345
+ {urlSubtype === RCS_CTA_URL_TYPE.DYNAMIC && (
346
+ <CapTooltipWithInfo
347
+ infoIconProps={{
348
+ style: { marginLeft: CAP_SPACE_04 },
349
+ }}
350
+ autoAdjustOverflow
351
+ placement="right"
352
+ title={formatMessage(messages.ctaDynamicUrlTooltip, { one: DYNAMIC_URL_SUFFIX })}
353
+ />
354
+ )}
279
355
  </CapHeading>
280
356
  <CapInput
281
357
  id={index}
358
+ addonAfter={
359
+ urlSubtype === RCS_CTA_URL_TYPE.DYNAMIC
360
+ && !(url || '').includes(DYNAMIC_URL_SUFFIX)
361
+ && DYNAMIC_URL_SUFFIX
362
+ }
282
363
  className="rcs-cta-url"
283
364
  onChange={onUrlChange}
284
- placeholder={formatMessage(messages.ctaStaticPlaceholder)}
285
- value={url}
365
+ placeholder={
366
+ urlSubtype === RCS_CTA_URL_TYPE.DYNAMIC
367
+ ? formatMessage(messages.ctaDynamicPlaceholder)
368
+ : formatMessage(messages.ctaEnterUrlPlaceholder)
369
+ }
370
+ value={url || ''}
286
371
  size="large"
287
372
  maxLength={URL_MAX_LENGTH}
288
373
  errorMessage={urlError}
374
+ suffix={renderInnerCharCount((url || '').length, URL_MAX_LENGTH)}
289
375
  />
290
- {renderLength(url.length, URL_MAX_LENGTH)}
291
376
  </CapColumn>
292
377
  </CapRow>
293
378
  <CapRow useLegacy className="rcs-cta-save-delete-btn">
@@ -297,7 +382,7 @@ export const CapActionButton = (props) => {
297
382
  }
298
383
  placement="bottom"
299
384
  >
300
- <div className="button-disabled-tooltip-wrapper">
385
+ <CapRow className="button-disabled-tooltip-wrapper">
301
386
  <CapButton
302
387
  onClick={() => saveCta(index)}
303
388
  disabled={ctaSaveDisabled(index)}
@@ -305,15 +390,17 @@ export const CapActionButton = (props) => {
305
390
  >
306
391
  {formatMessage(globalMessages.save)}
307
392
  </CapButton>
308
- </div>
393
+ </CapRow>
309
394
  </CapTooltip>
310
- <CapButton
311
- onClick={() => deleteButtonHandler(index)}
312
- className="rcs-cta-delete-btn"
313
- type="secondary"
314
- >
315
- {formatMessage(globalMessages.delete)}
316
- </CapButton>
395
+ {!hideDeleteForSuggestionIndex(index) && !cannotDeleteSavedMandatory(index) && (
396
+ <CapButton
397
+ onClick={() => deleteButtonHandler(index)}
398
+ className="rcs-cta-delete-btn"
399
+ type="secondary"
400
+ >
401
+ {formatMessage(globalMessages.delete)}
402
+ </CapButton>
403
+ )}
317
404
  </CapRow>
318
405
  </>
319
406
  )}
@@ -323,7 +410,7 @@ export const CapActionButton = (props) => {
323
410
  title={ctaSaveDisabled(index) && formatMessage(messages.ctaSaveDisabled)}
324
411
  placement="bottom"
325
412
  >
326
- <div className="button-disabled-tooltip-wrapper">
413
+ <CapRow className="button-disabled-tooltip-wrapper">
327
414
  <CapButton
328
415
  onClick={() => saveCta(index)}
329
416
  disabled={ctaSaveDisabled(index)}
@@ -331,9 +418,9 @@ export const CapActionButton = (props) => {
331
418
  >
332
419
  {formatMessage(globalMessages.save)}
333
420
  </CapButton>
334
- </div>
421
+ </CapRow>
335
422
  </CapTooltip>
336
- {index !== 0 && (
423
+ {!(isHostIcs && index === 0) && !hideDeleteForSuggestionIndex(index) && !cannotDeleteSavedMandatory(index) && (
337
424
  <CapButton
338
425
  onClick={() => deleteButtonHandler(index)}
339
426
  className="rcs-cta-delete-btn"
@@ -344,23 +431,24 @@ export const CapActionButton = (props) => {
344
431
  )}
345
432
  </CapRow>
346
433
  )}
347
- </div>
434
+ </CapRow>
348
435
  );
349
436
  } else {
350
437
  const ctaIsPhone = type === RCS_BUTTON_TYPES.PHONE_NUMBER;
351
438
  const ctaIsReply = type === RCS_BUTTON_TYPES.QUICK_REPLY;
352
- renderArray.push(
439
+ suggestions?.length > 0 && renderArray?.push(
353
440
  <CapRow
354
441
  key={`cta-saved-${index}`}
355
442
  className="cap-rcs-saved-cta margin-t-12"
356
443
  align="middle"
357
444
  type="flex"
445
+ gutter={9}
358
446
  >
359
447
  <CapColumn
360
448
  span={1}
361
449
  className={`${ctaIsPhone ? 'whatsapp-saved-cta-phone-icon' : ''}`}
362
450
  >
363
- <CapIcon size="s" type={ctaIsPhone ? 'call' : 'launch'} />
451
+ <CapIcon size="s" type={ctaIsPhone ? 'call' : (ctaIsReply ? 'small-link' : 'open-in-new')} />
364
452
  </CapColumn>
365
453
  <CapColumn span={6}>
366
454
  <CapLabel
@@ -370,49 +458,71 @@ export const CapActionButton = (props) => {
370
458
  {text}
371
459
  </CapLabel>
372
460
  </CapColumn>
373
- {ctaIsPhone && (
374
- <CapColumn span={10} align="left">
375
- <CapLabel className="phone">+{phoneNumber}</CapLabel>
376
- </CapColumn>
377
- )}
378
- {!ctaIsPhone && !ctaIsReply && (
379
- <>
380
- <CapColumn span={3}>
381
- <CapLabel className="url-type" type="label2">
382
- {formatMessage(messages.ctaWebsiteTypeStatic)}
383
- </CapLabel>
384
- </CapColumn>
385
- <CapRow type='flex' className="rcs-url-preview-wrapper">
386
- <CapTooltip title={url} placement="top">
387
- <CapColumn span={14} className="margin-r-0">
461
+ <CapColumn flex={1} className="rcs-saved-cta-value-column">
462
+ <div className="rcs-saved-cta-value-actions">
463
+ {ctaIsPhone && (
464
+ <CapLabel className="phone">+{phoneNumber}</CapLabel>
465
+ )}
466
+ {!ctaIsPhone && !ctaIsReply && (
467
+ <>
468
+ <CapLabel className="url-type" type="label2">
469
+ {(cta.urlType || RCS_CTA_URL_TYPE.STATIC) === RCS_CTA_URL_TYPE.DYNAMIC
470
+ ? formatMessage(messages.ctaWebsiteTypeDynamic)
471
+ : formatMessage(messages.ctaWebsiteTypeStatic)}
472
+ </CapLabel>
473
+ <CapTooltip title={url} placement="top">
388
474
  <CapLabel className="url">{url}</CapLabel>
389
- </CapColumn>
475
+ </CapTooltip>
476
+ </>
477
+ )}
478
+ {isEditFlow && urlSubtype === RCS_CTA_URL_TYPE.DYNAMIC && (url || '').includes(DYNAMIC_URL_SUFFIX) && (
479
+ <TagList
480
+ className="rcs-cta-taglist"
481
+ label={formatMessage(messages.ctaTagListLabel)}
482
+ onTagSelect={(data) => onTagSelect(data, index, url)}
483
+ location={location}
484
+ tags={tags}
485
+ injectedTags={injectedTags}
486
+ selectedOfferDetails={selectedOfferDetails}
487
+ onContextChange={onContextChange}
488
+ />
489
+ )}
490
+ {isEditFlow && urlSubtype === RCS_CTA_URL_TYPE.DYNAMIC && !(url || '').includes(DYNAMIC_URL_SUFFIX) && (
491
+ <CapTooltip title={formatMessage(messages.ctaTagListRevert)} placement="top">
492
+ <CapIcon
493
+ size="s"
494
+ type="return"
495
+ className="rcs-cta-tag-revert"
496
+ onClick={() => revertTagSelect(index, url)}
497
+ />
390
498
  </CapTooltip>
391
- </CapRow>
392
- </>
393
- )}
394
- {(isFullMode && !isEditFlow) && (
395
- <>
396
- <CapColumn
397
- span={1}
398
- className="rcs-saved-cta-edit-icon"
399
- onClick={() => editCta(index)}
400
- >
401
- <CapIcon size="s" type="edit" />
402
- </CapColumn>
403
- {index !== 0 && (
404
- <CapColumn className="rcs-saved-cta-delete-icon" span={1} onClick={() => deleteButtonHandler(index)}>
405
- <CapIcon size="s" type="delete" />
406
- </CapColumn>
407
499
  )}
408
- </>
409
- )}
500
+ {(isFullMode && !isEditFlow) && (
501
+ <div className="rcs-saved-cta-action-icons">
502
+ <CapIcon
503
+ size="s"
504
+ type="edit"
505
+ className="rcs-saved-cta-edit-icon"
506
+ onClick={() => editCta(index)}
507
+ />
508
+ {!(isHostIcs && index === 0) && !hideDeleteForSuggestionIndex(index) && !cannotDeleteSavedMandatory(index) && (
509
+ <CapIcon
510
+ size="s"
511
+ type="delete"
512
+ className="rcs-saved-cta-delete-icon"
513
+ onClick={() => deleteButtonHandler(index)}
514
+ />
515
+ )}
516
+ </div>
517
+ )}
518
+ </div>
519
+ </CapColumn>
410
520
  </CapRow>
411
521
  );
412
522
  }
413
523
  });
414
524
  {
415
- suggestions.length < maxButtons &&
525
+ suggestions?.length < maxButtons &&
416
526
  (isFullMode && !isEditFlow)
417
527
  && renderArray.push(
418
528
  <CapRow useLegacy>
@@ -422,7 +532,7 @@ export const CapActionButton = (props) => {
422
532
  }
423
533
  placement={'right'}
424
534
  >
425
- <div className="button-disabled-tooltip-wrapper">
535
+ <CapRow className="button-disabled-tooltip-wrapper">
426
536
  <CapButton
427
537
  type="flat"
428
538
  id="rcs-cta-add-button"
@@ -433,7 +543,7 @@ export const CapActionButton = (props) => {
433
543
  >
434
544
  {formatMessage(messages.addButton)}
435
545
  </CapButton>
436
- </div>
546
+ </CapRow>
437
547
  </CapTooltip>
438
548
  </CapRow>,
439
549
  );
@@ -446,6 +556,8 @@ export const CapActionButton = (props) => {
446
556
  CapActionButton.propTypes = {
447
557
  intl: intlShape.isRequired,
448
558
  updateButtonChange: PropTypes.func,
559
+ minSavedSuggestions: PropTypes.number,
560
+ hideDeleteSuggestionIndexes: PropTypes.arrayOf(PropTypes.number),
449
561
  suggestions: PropTypes.array,
450
562
  tags: PropTypes.array,
451
563
  injectedTags: PropTypes.object,
@@ -453,12 +565,15 @@ CapActionButton.propTypes = {
453
565
  selectedOfferDetails: PropTypes.array,
454
566
  onTagSelect: PropTypes.func,
455
567
  onContextChange: PropTypes.func,
568
+ host: PropTypes.string,
456
569
  };
457
570
 
458
571
  CapActionButton.defaultProps = {
459
572
  buttonTextlen: 20,
460
573
  type: '',
461
574
  updateButtonChange: () => {},
575
+ minSavedSuggestions: 0,
576
+ hideDeleteSuggestionIndexes: [],
462
577
  suggestions: [],
463
578
  tags: [],
464
579
  injectedTags: {},
@@ -466,6 +581,7 @@ CapActionButton.defaultProps = {
466
581
  selectedOfferDetails: [],
467
582
  onTagSelect: () => {},
468
583
  onContextChange: () => {},
584
+ host: '',
469
585
  };
470
586
 
471
587
  export default injectIntl(CapActionButton);