@capillarytech/creatives-library 9.0.29 → 9.0.31

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 (147) 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 +261 -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 +774 -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 +322 -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/MobilePush/Create/test/saga.test.js +2 -2
  96. package/v2Containers/Rcs/carouselUtils.js +224 -0
  97. package/v2Containers/Rcs/components/CarouselCard.js +317 -0
  98. package/v2Containers/Rcs/components/CarouselCardButtons.js +113 -0
  99. package/v2Containers/Rcs/components/CarouselCardMedia.js +136 -0
  100. package/v2Containers/Rcs/components/CarouselCharacterCount.js +31 -0
  101. package/v2Containers/Rcs/components/CarouselDimensionSelection.js +80 -0
  102. package/v2Containers/Rcs/constants.js +132 -16
  103. package/v2Containers/Rcs/index.js +1822 -946
  104. package/v2Containers/Rcs/index.scss +443 -8
  105. package/v2Containers/Rcs/messages.js +45 -22
  106. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +494 -0
  107. package/v2Containers/Rcs/tests/CarouselCard.test.js +464 -0
  108. package/v2Containers/Rcs/tests/CarouselCardButtons.test.js +211 -0
  109. package/v2Containers/Rcs/tests/CarouselCardMedia.test.js +160 -0
  110. package/v2Containers/Rcs/tests/CarouselCharacterCount.test.js +50 -0
  111. package/v2Containers/Rcs/tests/CarouselDimensionSelection.test.js +119 -0
  112. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +74440 -39387
  113. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +10 -74
  114. package/v2Containers/Rcs/tests/carouselUtils.test.js +916 -0
  115. package/v2Containers/Rcs/tests/index.test.js +275 -40
  116. package/v2Containers/Rcs/tests/mockData.js +38 -0
  117. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +912 -0
  118. package/v2Containers/Rcs/tests/utils.test.js +682 -30
  119. package/v2Containers/Rcs/utils.js +514 -12
  120. package/v2Containers/Sms/Create/index.js +115 -48
  121. package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
  122. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  123. package/v2Containers/SmsTrai/Create/index.js +9 -4
  124. package/v2Containers/SmsTrai/Edit/constants.js +2 -0
  125. package/v2Containers/SmsTrai/Edit/index.js +678 -169
  126. package/v2Containers/SmsTrai/Edit/index.scss +126 -0
  127. package/v2Containers/SmsTrai/Edit/messages.js +14 -4
  128. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +5615 -3014
  129. package/v2Containers/SmsWrapper/index.js +37 -8
  130. package/v2Containers/TagList/index.js +5 -0
  131. package/v2Containers/Templates/TemplatesActionBar.js +101 -0
  132. package/v2Containers/Templates/_templates.scss +196 -12
  133. package/v2Containers/Templates/actions.js +11 -0
  134. package/v2Containers/Templates/constants.js +2 -0
  135. package/v2Containers/Templates/index.js +131 -59
  136. package/v2Containers/Templates/sagas.js +57 -13
  137. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +129 -0
  138. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1060 -1015
  139. package/v2Containers/Templates/tests/sagas.test.js +199 -16
  140. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
  141. package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
  142. package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
  143. package/v2Containers/TemplatesV2/index.js +88 -25
  144. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
  145. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
  146. package/v2Containers/Whatsapp/index.js +3 -20
  147. 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,47 @@ 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;
233
+ const isDynamicUrlSubtype = urlSubtype === RCS_CTA_URL_TYPE.DYNAMIC;
185
234
  const phoneNumber = type !== RCS_BUTTON_TYPES.PHONE_NUMBER ? null : cta.phoneNumber;
186
235
  if (isFullMode && !isEditFlow && !isSaved) {
187
236
  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">
237
+ <CapRow className="rcs-button-cta-create-container" key={`cta-edit-${index}`} gutter={0}>
238
+ <CapRow className="rcs-button-cta-create" gutter={0}>
239
+ <CapColumn span={24}>
240
+ <CapHeading type="h4">
193
241
  {formatMessage(messages.ctaType)}
194
242
  </CapHeading>
195
- <CapSelect
196
- id="rcs-cta-type"
197
- options={ctaOptions || []}
198
- onChange={(value) => onCtaTypeChange(value, index)}
243
+ <CapRadioGroup
244
+ id={`rcs-cta-type-radio-${index}`}
245
+ className="cap-rcs-cta-type-radio"
246
+ options={CTA_TYPE_RADIO_OPTIONS}
199
247
  value={type}
200
- disabled={false}
248
+ onChange={(e) => onCtaTypeChange(e.target.value, index)}
201
249
  />
202
250
  </CapColumn>
203
- <CapColumn span={12}>
251
+ <CapColumn span={24}>
204
252
  <CapHeading type="h4" className="cta-label">
205
253
  {formatMessage(messages.ctaButtonText)}
206
254
  <CapTooltipWithInfo
@@ -221,28 +269,34 @@ export const CapActionButton = (props) => {
221
269
  size="large"
222
270
  maxLength={BTN_MAX_LENGTH}
223
271
  errorMessage={buttonError}
272
+ suffix={renderInnerCharCount((text || '').length, BTN_MAX_LENGTH)}
224
273
  />
225
- {renderLength(text.length, BTN_MAX_LENGTH)}
226
274
  </CapColumn>
227
275
  </CapRow>
228
276
  {type === RCS_BUTTON_TYPES.PHONE_NUMBER && (
229
277
  <>
230
278
  <CapRow>
231
- <CapColumn span={11}>
232
- {/* phone number */}
279
+ <CapColumn span={24}>
233
280
  <CapHeading type="h4" className="cta-label">
234
281
  {formatMessage(messages.ctaPhoneNo)}
235
282
  </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)}
283
+ <CapRow
284
+ className="rcs-cta-input-with-inner-count rcs-cta-input-with-inner-count--phone"
285
+ gutter={0}
286
+ >
287
+ <CapColumn span={24}>
288
+ <PhoneInput
289
+ placeholder={formatMessage(messages.ctaPhoneNoPlaceholder)}
290
+ autoFormat={false}
291
+ countryCodeEditable={false}
292
+ value={phoneNumber}
293
+ onChange={(value) => onPhoneNoChange(value, index)}
294
+ country="in"
295
+ className="cta-phone-number rcs-cta-phone-input"
296
+ />
297
+ {renderInnerCharCount((phoneNumber || '').length, PHONE_NUMBER_MAX_LENGTH)}
298
+ </CapColumn>
299
+ </CapRow>
246
300
  </CapColumn>
247
301
  </CapRow>
248
302
  <CapRow useLegacy className="rcs-cta-save-delete-btn">
@@ -250,7 +304,7 @@ export const CapActionButton = (props) => {
250
304
  title={ctaSaveDisabled(index) && formatMessage(messages.ctaSaveDisabled)}
251
305
  placement="bottom"
252
306
  >
253
- <div className="button-disabled-tooltip-wrapper">
307
+ <CapRow className="button-disabled-tooltip-wrapper">
254
308
  <CapButton
255
309
  onClick={() => saveCta(index)}
256
310
  disabled={ctaSaveDisabled(index)}
@@ -258,36 +312,68 @@ export const CapActionButton = (props) => {
258
312
  >
259
313
  {formatMessage(globalMessages.save)}
260
314
  </CapButton>
261
- </div>
315
+ </CapRow>
262
316
  </CapTooltip>
263
- <CapButton
264
- onClick={() => deleteButtonHandler(index)}
265
- className="rcs-cta-delete-btn"
266
- type="secondary"
267
- >
268
- {formatMessage(globalMessages.delete)}
269
- </CapButton>
317
+ {!hideDeleteForSuggestionIndex(index) && !cannotDeleteSavedMandatory(index) && (
318
+ <CapButton
319
+ onClick={() => deleteButtonHandler(index)}
320
+ className="rcs-cta-delete-btn"
321
+ type="secondary"
322
+ >
323
+ {formatMessage(globalMessages.delete)}
324
+ </CapButton>
325
+ )}
270
326
  </CapRow>
271
327
  </>
272
328
  )}
273
329
  {type === RCS_BUTTON_TYPES.CTA && (
274
330
  <>
275
- <CapRow>
276
- <CapColumn span={24}>
331
+ <CapRow className="rcs-cta-url-fields-row" gutter={8}>
332
+ <CapColumn span={6} className="rcs-cta-url-type-col">
333
+ <CapHeading type="h4" className="cta-label">
334
+ {formatMessage(messages.ctaWebsiteType)}
335
+ </CapHeading>
336
+ <CapSelect
337
+ id={`rcs-cta-url-type-${index}`}
338
+ options={CTA_URL_TYPE_SELECT_OPTIONS}
339
+ value={urlSubtype}
340
+ onChange={(value) => onUrlSubtypeChange(value, index)}
341
+ />
342
+ </CapColumn>
343
+ <CapColumn span={18} className="rcs-cta-url-value-col">
277
344
  <CapHeading type="h4" className="cta-label">
278
- {formatMessage(messages.ctaWebsite)}
345
+ {formatMessage(messages.ctaWebsiteUrl)}
346
+ {isDynamicUrlSubtype && (
347
+ <CapTooltipWithInfo
348
+ infoIconProps={{
349
+ style: { marginLeft: CAP_SPACE_04 },
350
+ }}
351
+ autoAdjustOverflow
352
+ placement="right"
353
+ title={formatMessage(messages.ctaDynamicUrlTooltip, { one: DYNAMIC_URL_SUFFIX })}
354
+ />
355
+ )}
279
356
  </CapHeading>
280
357
  <CapInput
281
358
  id={index}
359
+ addonAfter={
360
+ isDynamicUrlSubtype
361
+ && !(url || '').includes(DYNAMIC_URL_SUFFIX)
362
+ && DYNAMIC_URL_SUFFIX
363
+ }
282
364
  className="rcs-cta-url"
283
365
  onChange={onUrlChange}
284
- placeholder={formatMessage(messages.ctaStaticPlaceholder)}
285
- value={url}
366
+ placeholder={
367
+ isDynamicUrlSubtype
368
+ ? formatMessage(messages.ctaDynamicPlaceholder)
369
+ : formatMessage(messages.ctaEnterUrlPlaceholder)
370
+ }
371
+ value={url || ''}
286
372
  size="large"
287
373
  maxLength={URL_MAX_LENGTH}
288
374
  errorMessage={urlError}
375
+ suffix={renderInnerCharCount((url || '').length, URL_MAX_LENGTH)}
289
376
  />
290
- {renderLength(url.length, URL_MAX_LENGTH)}
291
377
  </CapColumn>
292
378
  </CapRow>
293
379
  <CapRow useLegacy className="rcs-cta-save-delete-btn">
@@ -297,7 +383,7 @@ export const CapActionButton = (props) => {
297
383
  }
298
384
  placement="bottom"
299
385
  >
300
- <div className="button-disabled-tooltip-wrapper">
386
+ <CapRow className="button-disabled-tooltip-wrapper">
301
387
  <CapButton
302
388
  onClick={() => saveCta(index)}
303
389
  disabled={ctaSaveDisabled(index)}
@@ -305,15 +391,17 @@ export const CapActionButton = (props) => {
305
391
  >
306
392
  {formatMessage(globalMessages.save)}
307
393
  </CapButton>
308
- </div>
394
+ </CapRow>
309
395
  </CapTooltip>
310
- <CapButton
311
- onClick={() => deleteButtonHandler(index)}
312
- className="rcs-cta-delete-btn"
313
- type="secondary"
314
- >
315
- {formatMessage(globalMessages.delete)}
316
- </CapButton>
396
+ {!hideDeleteForSuggestionIndex(index) && !cannotDeleteSavedMandatory(index) && (
397
+ <CapButton
398
+ onClick={() => deleteButtonHandler(index)}
399
+ className="rcs-cta-delete-btn"
400
+ type="secondary"
401
+ >
402
+ {formatMessage(globalMessages.delete)}
403
+ </CapButton>
404
+ )}
317
405
  </CapRow>
318
406
  </>
319
407
  )}
@@ -323,7 +411,7 @@ export const CapActionButton = (props) => {
323
411
  title={ctaSaveDisabled(index) && formatMessage(messages.ctaSaveDisabled)}
324
412
  placement="bottom"
325
413
  >
326
- <div className="button-disabled-tooltip-wrapper">
414
+ <CapRow className="button-disabled-tooltip-wrapper">
327
415
  <CapButton
328
416
  onClick={() => saveCta(index)}
329
417
  disabled={ctaSaveDisabled(index)}
@@ -331,9 +419,9 @@ export const CapActionButton = (props) => {
331
419
  >
332
420
  {formatMessage(globalMessages.save)}
333
421
  </CapButton>
334
- </div>
422
+ </CapRow>
335
423
  </CapTooltip>
336
- {index !== 0 && (
424
+ {!(isHostIcs && index === 0) && !hideDeleteForSuggestionIndex(index) && !cannotDeleteSavedMandatory(index) && (
337
425
  <CapButton
338
426
  onClick={() => deleteButtonHandler(index)}
339
427
  className="rcs-cta-delete-btn"
@@ -344,23 +432,24 @@ export const CapActionButton = (props) => {
344
432
  )}
345
433
  </CapRow>
346
434
  )}
347
- </div>
435
+ </CapRow>
348
436
  );
349
437
  } else {
350
438
  const ctaIsPhone = type === RCS_BUTTON_TYPES.PHONE_NUMBER;
351
439
  const ctaIsReply = type === RCS_BUTTON_TYPES.QUICK_REPLY;
352
- renderArray.push(
440
+ suggestions?.length > 0 && renderArray?.push(
353
441
  <CapRow
354
442
  key={`cta-saved-${index}`}
355
443
  className="cap-rcs-saved-cta margin-t-12"
356
444
  align="middle"
357
445
  type="flex"
446
+ gutter={9}
358
447
  >
359
448
  <CapColumn
360
449
  span={1}
361
450
  className={`${ctaIsPhone ? 'whatsapp-saved-cta-phone-icon' : ''}`}
362
451
  >
363
- <CapIcon size="s" type={ctaIsPhone ? 'call' : 'launch'} />
452
+ <CapIcon size="s" type={ctaIsPhone ? 'call' : (ctaIsReply ? 'small-link' : 'open-in-new')} />
364
453
  </CapColumn>
365
454
  <CapColumn span={6}>
366
455
  <CapLabel
@@ -370,49 +459,71 @@ export const CapActionButton = (props) => {
370
459
  {text}
371
460
  </CapLabel>
372
461
  </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">
462
+ <CapColumn flex={1} className="rcs-saved-cta-value-column">
463
+ <div className="rcs-saved-cta-value-actions">
464
+ {ctaIsPhone && (
465
+ <CapLabel className="phone">+{phoneNumber}</CapLabel>
466
+ )}
467
+ {!ctaIsPhone && !ctaIsReply && (
468
+ <>
469
+ <CapLabel className="url-type" type="label2">
470
+ {(cta.urlType || RCS_CTA_URL_TYPE.STATIC) === RCS_CTA_URL_TYPE.DYNAMIC
471
+ ? formatMessage(messages.ctaWebsiteTypeDynamic)
472
+ : formatMessage(messages.ctaWebsiteTypeStatic)}
473
+ </CapLabel>
474
+ <CapTooltip title={url} placement="top">
388
475
  <CapLabel className="url">{url}</CapLabel>
389
- </CapColumn>
476
+ </CapTooltip>
477
+ </>
478
+ )}
479
+ {isDynamicUrlSubtype && (url || '').includes(DYNAMIC_URL_SUFFIX) && (
480
+ <TagList
481
+ className="rcs-cta-taglist"
482
+ label={formatMessage(messages.ctaTagListLabel)}
483
+ onTagSelect={(data) => onTagSelect(data, index, url)}
484
+ location={location}
485
+ tags={tags}
486
+ injectedTags={injectedTags}
487
+ selectedOfferDetails={selectedOfferDetails}
488
+ onContextChange={onContextChange}
489
+ />
490
+ )}
491
+ {isDynamicUrlSubtype && !(url || '').includes(DYNAMIC_URL_SUFFIX) && (
492
+ <CapTooltip title={formatMessage(messages.ctaTagListRevert)} placement="top">
493
+ <CapIcon
494
+ size="s"
495
+ type="return"
496
+ className="rcs-cta-tag-revert"
497
+ onClick={() => revertTagSelect(index, url)}
498
+ />
390
499
  </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
500
  )}
408
- </>
409
- )}
501
+ {(isFullMode && !isEditFlow) && (
502
+ <div className="rcs-saved-cta-action-icons">
503
+ <CapIcon
504
+ size="s"
505
+ type="edit"
506
+ className="rcs-saved-cta-edit-icon"
507
+ onClick={() => editCta(index)}
508
+ />
509
+ {!(isHostIcs && index === 0) && !hideDeleteForSuggestionIndex(index) && !cannotDeleteSavedMandatory(index) && (
510
+ <CapIcon
511
+ size="s"
512
+ type="delete"
513
+ className="rcs-saved-cta-delete-icon"
514
+ onClick={() => deleteButtonHandler(index)}
515
+ />
516
+ )}
517
+ </div>
518
+ )}
519
+ </div>
520
+ </CapColumn>
410
521
  </CapRow>
411
522
  );
412
523
  }
413
524
  });
414
525
  {
415
- suggestions.length < maxButtons &&
526
+ suggestions?.length < maxButtons &&
416
527
  (isFullMode && !isEditFlow)
417
528
  && renderArray.push(
418
529
  <CapRow useLegacy>
@@ -422,7 +533,7 @@ export const CapActionButton = (props) => {
422
533
  }
423
534
  placement={'right'}
424
535
  >
425
- <div className="button-disabled-tooltip-wrapper">
536
+ <CapRow className="button-disabled-tooltip-wrapper">
426
537
  <CapButton
427
538
  type="flat"
428
539
  id="rcs-cta-add-button"
@@ -433,7 +544,7 @@ export const CapActionButton = (props) => {
433
544
  >
434
545
  {formatMessage(messages.addButton)}
435
546
  </CapButton>
436
- </div>
547
+ </CapRow>
437
548
  </CapTooltip>
438
549
  </CapRow>,
439
550
  );
@@ -446,6 +557,8 @@ export const CapActionButton = (props) => {
446
557
  CapActionButton.propTypes = {
447
558
  intl: intlShape.isRequired,
448
559
  updateButtonChange: PropTypes.func,
560
+ minSavedSuggestions: PropTypes.number,
561
+ hideDeleteSuggestionIndexes: PropTypes.arrayOf(PropTypes.number),
449
562
  suggestions: PropTypes.array,
450
563
  tags: PropTypes.array,
451
564
  injectedTags: PropTypes.object,
@@ -453,12 +566,15 @@ CapActionButton.propTypes = {
453
566
  selectedOfferDetails: PropTypes.array,
454
567
  onTagSelect: PropTypes.func,
455
568
  onContextChange: PropTypes.func,
569
+ host: PropTypes.string,
456
570
  };
457
571
 
458
572
  CapActionButton.defaultProps = {
459
573
  buttonTextlen: 20,
460
574
  type: '',
461
575
  updateButtonChange: () => {},
576
+ minSavedSuggestions: 0,
577
+ hideDeleteSuggestionIndexes: [],
462
578
  suggestions: [],
463
579
  tags: [],
464
580
  injectedTags: {},
@@ -466,6 +582,7 @@ CapActionButton.defaultProps = {
466
582
  selectedOfferDetails: [],
467
583
  onTagSelect: () => {},
468
584
  onContextChange: () => {},
585
+ host: '',
469
586
  };
470
587
 
471
588
  export default injectIntl(CapActionButton);