@capillarytech/creatives-library 9.0.15-alpha.1 → 9.0.16

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 (132) hide show
  1. package/constants/unified.js +0 -29
  2. package/global-styles.js +1 -1
  3. package/package.json +1 -1
  4. package/services/tests/api.test.js +20 -35
  5. package/utils/commonUtils.js +1 -19
  6. package/v2Components/CapActionButton/constants.js +0 -7
  7. package/v2Components/CapActionButton/index.js +109 -168
  8. package/v2Components/CapActionButton/index.scss +5 -156
  9. package/v2Components/CapActionButton/messages.js +3 -19
  10. package/v2Components/CapActionButton/tests/index.test.js +18 -42
  11. package/v2Components/CapImageUpload/index.js +2 -2
  12. package/v2Components/CapTagList/index.js +0 -10
  13. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +48 -70
  14. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
  15. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -214
  16. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
  17. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +9 -83
  18. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
  19. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -60
  20. package/v2Components/CommonTestAndPreview/SendTestMessage.js +5 -10
  21. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +23 -201
  22. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +76 -355
  23. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +8 -155
  24. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +0 -11
  25. package/v2Components/CommonTestAndPreview/constants.js +2 -38
  26. package/v2Components/CommonTestAndPreview/index.js +222 -761
  27. package/v2Components/CommonTestAndPreview/messages.js +3 -45
  28. package/v2Components/CommonTestAndPreview/sagas.js +6 -25
  29. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +0 -1
  30. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +267 -284
  31. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +65 -231
  32. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +5 -118
  33. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -341
  34. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +1 -8
  35. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +13 -34
  36. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +283 -281
  37. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
  38. package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -133
  39. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +24 -31
  40. package/v2Components/TemplatePreview/_templatePreview.scss +22 -41
  41. package/v2Components/TemplatePreview/index.js +31 -143
  42. package/v2Components/TemplatePreview/tests/index.test.js +0 -142
  43. package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +6 -6
  44. package/v2Components/TestAndPreviewSlidebox/index.js +1 -13
  45. package/v2Components/TestAndPreviewSlidebox/sagas.js +4 -11
  46. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +1 -3
  47. package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +1 -4
  48. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +0 -4
  49. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +20 -20
  50. package/v2Containers/CreativesContainer/SlideBoxContent.js +7 -36
  51. package/v2Containers/CreativesContainer/SlideBoxFooter.js +5 -14
  52. package/v2Containers/CreativesContainer/SlideBoxHeader.js +5 -36
  53. package/v2Containers/CreativesContainer/constants.js +0 -11
  54. package/v2Containers/CreativesContainer/index.js +104 -323
  55. package/v2Containers/CreativesContainer/index.scss +1 -83
  56. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -79
  57. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
  58. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
  59. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +90 -333
  60. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +15 -20
  61. package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
  62. package/v2Containers/Email/index.js +5 -2
  63. package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
  64. package/v2Containers/Rcs/constants.js +11 -131
  65. package/v2Containers/Rcs/index.js +810 -2605
  66. package/v2Containers/Rcs/index.scss +8 -289
  67. package/v2Containers/Rcs/messages.js +3 -34
  68. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +38537 -73535
  69. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +8 -0
  70. package/v2Containers/Rcs/tests/index.test.js +36 -147
  71. package/v2Containers/Rcs/tests/mockData.js +0 -38
  72. package/v2Containers/Rcs/tests/utils.test.js +30 -646
  73. package/v2Containers/Rcs/utils.js +11 -478
  74. package/v2Containers/Sms/Create/index.js +48 -115
  75. package/v2Containers/SmsTrai/Create/index.js +4 -9
  76. package/v2Containers/SmsTrai/Edit/constants.js +0 -2
  77. package/v2Containers/SmsTrai/Edit/index.js +169 -678
  78. package/v2Containers/SmsTrai/Edit/messages.js +4 -14
  79. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2854 -5455
  80. package/v2Containers/SmsWrapper/index.js +8 -37
  81. package/v2Containers/TagList/index.js +0 -6
  82. package/v2Containers/Templates/_templates.scss +12 -171
  83. package/v2Containers/Templates/actions.js +0 -11
  84. package/v2Containers/Templates/constants.js +0 -2
  85. package/v2Containers/Templates/index.js +55 -125
  86. package/v2Containers/Templates/sagas.js +13 -57
  87. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1015 -1060
  88. package/v2Containers/Templates/tests/sagas.test.js +16 -199
  89. package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -72
  90. package/v2Containers/TemplatesV2/index.js +23 -86
  91. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
  92. package/v2Containers/Whatsapp/index.js +20 -3
  93. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -578
  94. package/utils/rcsPayloadUtils.js +0 -92
  95. package/utils/templateVarUtils.js +0 -201
  96. package/utils/tests/rcsPayloadUtils.test.js +0 -226
  97. package/utils/tests/templateVarUtils.test.js +0 -204
  98. package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
  99. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
  100. package/v2Components/CommonTestAndPreview/utils.js +0 -84
  101. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -91
  102. package/v2Components/SmsFallback/constants.js +0 -94
  103. package/v2Components/SmsFallback/index.js +0 -958
  104. package/v2Components/SmsFallback/index.scss +0 -266
  105. package/v2Components/SmsFallback/messages.js +0 -78
  106. package/v2Components/SmsFallback/smsFallbackUtils.js +0 -120
  107. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
  108. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
  109. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
  110. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -208
  111. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -309
  112. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
  113. package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
  114. package/v2Components/TemplatePreview/constants.js +0 -2
  115. package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
  116. package/v2Components/VarSegmentMessageEditor/index.js +0 -125
  117. package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
  118. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -17
  119. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -79
  120. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
  121. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
  122. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
  123. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -227
  124. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -318
  125. package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
  126. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
  127. package/v2Containers/SmsTrai/Edit/index.scss +0 -126
  128. package/v2Containers/Templates/TemplatesActionBar.js +0 -101
  129. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
  130. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
  131. package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
  132. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +0 -131
@@ -9,72 +9,29 @@
9
9
 
10
10
  import get from 'lodash/get';
11
11
  import { CHANNELS } from '../../constants';
12
- import { RCS_CONTACT_INFO_GSM_TYPE_KEYS } from '../constants';
13
- import { typeMatches, unwrapEntity } from '../../utils';
14
12
 
15
- /**
16
- * Include row unless explicitly invalid — many APIs omit `valid`, which would
17
- * otherwise filter out every sender (RCS/SMS lists empty in UI).
18
- */
19
13
  const getSenderOptions = (contactInfo, key) =>
20
14
  (contactInfo || [])
21
- .filter((contactInfoItem) => typeMatches(contactInfoItem.type, key) && contactInfoItem.valid !== false)
15
+ .filter((contactInfoItem) => contactInfoItem.type === key && contactInfoItem.valid)
22
16
  .sort((contact) => (contact.default ? -1 : 0));
23
17
 
24
- /** RCS APIs may label senders under several `contactInfo.type` keys — see `RCS_CONTACT_INFO_GSM_TYPE_KEYS`. */
25
- const getRcsLikeGsmSenders = (contactInfo) => {
26
- const seen = new Set();
27
- const mergedSenderRows = [];
28
- RCS_CONTACT_INFO_GSM_TYPE_KEYS.forEach((key) => {
29
- getSenderOptions(contactInfo, key).forEach((row) => {
30
- const senderValue = row?.value;
31
- // Skip empty/whitespace sender values to avoid polluting the RCS sender dropdown.
32
- if (senderValue == null || String(senderValue).trim() === '' || seen.has(senderValue)) return;
33
- seen.add(senderValue);
34
- mergedSenderRows.push(row);
35
- });
36
- });
37
- return mergedSenderRows.sort((a, b) => (a.default ? -1 : 0) - (b.default ? -1 : 0));
38
- };
39
-
40
18
  /**
41
19
  * Parse raw API response for one channel into { domains: [...] }
42
- * @param {string} channel - SMS | EMAIL | WHATSAPP | RCS
20
+ * @param {string} channel - SMS | EMAIL | WHATSAPP
43
21
  * @param {Object} response - API response (e.g. { entity: { SMS: [...] } } or { entity: [...] })
44
22
  * @returns {{ domains: Array }} - domains array compatible with campaigns DeliverySettingsV2
45
23
  */
46
24
  export function parseSenderDetailsResponse(channel, response) {
47
- const entity = unwrapEntity(response);
48
- if (entity == null || entity === '') {
25
+ const entity = get(response, 'entity', response);
26
+ if (!entity) {
49
27
  return { domains: [] };
50
28
  }
51
29
 
52
- // Normalize once: entity keys may be any casing (SMS/sms/rcs) but downstream branches use CHANNELS.* (uppercase).
53
- const normalizedChannel =
54
- channel == null || String(channel).trim() === ''
55
- ? ''
56
- : String(channel).trim().toUpperCase();
57
-
58
- // Single-channel API: entity may be { [channel]: [...] } or direct array.
59
- // Some responses use different casing (e.g. rcs vs RCS) or a single domain object instead of an array.
60
- let channelSenderDetails =
61
- get(entity, channel, null)
62
- ?? (normalizedChannel ? get(entity, normalizedChannel, null) : null)
63
- ?? (normalizedChannel ? get(entity, normalizedChannel.toLowerCase(), null) : null);
64
-
30
+ // Single-channel API: entity may be { [channel]: [...] } or direct array
31
+ let channelSenderDetails = get(entity, channel, null);
65
32
  if (channelSenderDetails == null && Array.isArray(entity)) {
66
33
  channelSenderDetails = entity;
67
34
  }
68
-
69
- if (channelSenderDetails != null && !Array.isArray(channelSenderDetails)) {
70
- const single = channelSenderDetails;
71
- if (single && typeof single === 'object' && single.domainProperties) {
72
- channelSenderDetails = [single];
73
- } else {
74
- return { domains: [] };
75
- }
76
- }
77
-
78
35
  if (!Array.isArray(channelSenderDetails)) {
79
36
  return { domains: [] };
80
37
  }
@@ -85,7 +42,6 @@ export function parseSenderDetailsResponse(channel, response) {
85
42
  const { id: dgmId, priority, domainProperties = {} } = element;
86
43
  const {
87
44
  domainName,
88
- hostName,
89
45
  id = -1,
90
46
  contactInfo,
91
47
  connectionProperties: {
@@ -95,25 +51,21 @@ export function parseSenderDetailsResponse(channel, response) {
95
51
  } = {},
96
52
  } = domainProperties;
97
53
 
98
- const resolvedDomainName = domainName || hostName;
99
-
100
54
  const domain = {
101
55
  dgmId,
102
- domainName: resolvedDomainName,
56
+ domainName,
103
57
  domainId: id,
104
58
  priority: priority != null ? priority : 0,
105
59
  };
106
60
 
107
- if ([CHANNELS.SMS, CHANNELS.WHATSAPP, CHANNELS.RCS].includes(normalizedChannel)) {
61
+ if ([CHANNELS.SMS, CHANNELS.WHATSAPP].includes(channel)) {
108
62
  domain.cdmaSenders = getSenderOptions(contactInfo, 'cdma_sender_id');
109
- domain.gsmSenders = normalizedChannel === CHANNELS.RCS
110
- ? getRcsLikeGsmSenders(contactInfo)
111
- : getSenderOptions(contactInfo, 'gsm_sender_id');
63
+ domain.gsmSenders = getSenderOptions(contactInfo, 'gsm_sender_id');
112
64
  domain.sourceAccountIdentifier =
113
65
  sourceAccountIdentifier || wabaId || userid || '';
114
66
  }
115
67
 
116
- if (normalizedChannel === CHANNELS.EMAIL) {
68
+ if (channel === CHANNELS.EMAIL) {
117
69
  domain.emailSenders = getSenderOptions(contactInfo, 'sender_id');
118
70
  domain.emailRepliers = getSenderOptions(contactInfo, 'reply_to_id');
119
71
  }
@@ -124,8 +76,7 @@ export function parseSenderDetailsResponse(channel, response) {
124
76
  // Sort by priority and dedupe by domainName (match campaigns behaviour)
125
77
  domains.sort((a, b) => (a.priority || 0) - (b.priority || 0));
126
78
  const deduped = domains.reduce((acc, domain) => {
127
- const key = domain.domainName ?? domain.domainId;
128
- if (!acc.find((existing) => (existing.domainName ?? existing.domainId) === key)) {
79
+ if (!acc.find((d) => d.domainName === domain.domainName)) {
129
80
  acc.push(domain);
130
81
  }
131
82
  return acc;
@@ -6,13 +6,12 @@ import CapButton from '@capillarytech/cap-ui-library/CapButton';
6
6
  import CapHeader from '@capillarytech/cap-ui-library/CapHeader';
7
7
  import CapStepsAccordian from '@capillarytech/cap-ui-library/CapStepsAccordian';
8
8
  import CapTreeSelect from '@capillarytech/cap-ui-library/CapTreeSelect';
9
- import CapSelect from '@capillarytech/cap-ui-library/CapSelect';
10
9
  import isEmpty from 'lodash/isEmpty';
11
10
  import messages from './messages';
12
11
  import DeliverySettings from './DeliverySettings';
13
12
  import { CHANNELS } from './constants';
14
13
 
15
- const CHANNELS_WITH_DELIVERY_SETTINGS = [CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP, CHANNELS.RCS];
14
+ const CHANNELS_WITH_DELIVERY_SETTINGS = [CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP];
16
15
 
17
16
  const SendTestMessage = ({
18
17
  isFetchingTestCustomers,
@@ -29,13 +28,12 @@ const SendTestMessage = ({
29
28
  searchValue,
30
29
  setSearchValue,
31
30
  deliverySettings,
32
- senderDetailsByChannel = {},
31
+ senderDetailsOptions,
33
32
  wecrmAccounts,
34
33
  onSaveDeliverySettings,
35
34
  isLoadingSenderDetails,
36
35
  smsTraiDltEnabled,
37
36
  registeredSenderIds,
38
- isChannelSmsFallbackPreviewEnabled,
39
37
  }) => {
40
38
  const addCustomerContent = renderAddTestCustomerButton ? renderAddTestCustomerButton() : null;
41
39
  return (
@@ -79,14 +77,13 @@ const SendTestMessage = ({
79
77
  <DeliverySettings
80
78
  channel={channel}
81
79
  deliverySettings={deliverySettings || {}}
82
- senderDetailsByChannel={senderDetailsByChannel}
80
+ senderDetailsOptions={senderDetailsOptions}
83
81
  wecrmAccounts={wecrmAccounts || []}
84
82
  onSaveDeliverySettings={onSaveDeliverySettings}
85
83
  isLoadingSenderDetails={isLoadingSenderDetails}
86
84
  formatMessage={formatMessage}
87
85
  smsTraiDltEnabled={smsTraiDltEnabled}
88
86
  registeredSenderIds={registeredSenderIds}
89
- isChannelSmsFallbackPreviewEnabled={isChannelSmsFallbackPreviewEnabled}
90
87
  whatsappAccountFromForm={
91
88
  channel === CHANNELS.WHATSAPP && formData?.accountName
92
89
  ? { accountName: formData.accountName }
@@ -124,13 +121,12 @@ SendTestMessage.propTypes = {
124
121
  searchValue: PropTypes.string,
125
122
  setSearchValue: PropTypes.func,
126
123
  deliverySettings: PropTypes.object,
127
- senderDetailsByChannel: PropTypes.object,
124
+ senderDetailsOptions: PropTypes.array,
128
125
  wecrmAccounts: PropTypes.array,
129
126
  onSaveDeliverySettings: PropTypes.func,
130
127
  isLoadingSenderDetails: PropTypes.bool,
131
128
  smsTraiDltEnabled: PropTypes.bool,
132
129
  registeredSenderIds: PropTypes.array,
133
- isChannelSmsFallbackPreviewEnabled: PropTypes.bool,
134
130
  };
135
131
 
136
132
  SendTestMessage.defaultProps = {
@@ -140,13 +136,12 @@ SendTestMessage.defaultProps = {
140
136
  searchValue: '',
141
137
  setSearchValue: () => {}, // no-op when not provided; required by TreeSelect when showSearch is true
142
138
  deliverySettings: {},
143
- senderDetailsByChannel: {},
139
+ senderDetailsOptions: [],
144
140
  wecrmAccounts: [],
145
141
  onSaveDeliverySettings: undefined,
146
142
  isLoadingSenderDetails: false,
147
143
  smsTraiDltEnabled: false,
148
144
  registeredSenderIds: [],
149
- isChannelSmsFallbackPreviewEnabled: false,
150
145
  };
151
146
 
152
147
  export default SendTestMessage;
@@ -25,7 +25,6 @@ import {
25
25
  TIME_FORMAT_MINUTE_PADDING_THRESHOLD,
26
26
  TIME_FORMAT_AM,
27
27
  TIME_FORMAT_PM,
28
- MEDIA_TYPE_VIDEO,
29
28
  } from '../constants';
30
29
  import messages from '../messages';
31
30
  import { RCS_BUTTON_TYPES } from '../../../v2Containers/Rcs/constants';
@@ -51,57 +50,41 @@ const RcsPreviewContent = ({
51
50
  const deviceName = device === ANDROID ? ANDROID_DEVICE_NAME : IOS_DEVICE_NAME;
52
51
 
53
52
  // Render RCS suggestions (Quick Reply, CTA, Phone Number)
54
- // options.isCarousel: stacked bars below card body with gap (Figma), no dividers between CTAs
55
- const renderRcsSuggestionsPreview = (suggestionsArg, options = {}) => {
56
- const { isCarousel = false } = options;
53
+ const renderRcsSuggestionsPreview = () => {
57
54
  const renderArray = [];
58
- const suggestions = Array.isArray(suggestionsArg)
59
- ? suggestionsArg
60
- : (content?.suggestions || []);
55
+ const { suggestions = [] } = content || {};
61
56
 
62
57
  if (suggestions.length === 0) {
63
58
  return null;
64
59
  }
65
60
 
66
- const ctaClass = isCarousel ? 'rcs-cta-preview rcs-cta-preview--carousel-bar' : 'rcs-cta-preview';
67
-
68
61
  suggestions.forEach((suggestion) => {
69
62
  const { type, text } = suggestion || {};
70
63
  const suggestionKey = `${type}-${text || Math.random()}`;
71
64
 
72
- if (renderArray.length > 0 && !isCarousel) {
65
+ if (renderArray.length > 0) {
73
66
  renderArray.push(<CapDivider key={`divider-${suggestionKey}`} className="whatsapp-divider" />);
74
67
  }
75
68
 
76
69
  if (type === RCS_BUTTON_TYPES.QUICK_REPLY) {
77
70
  renderArray.push(
78
- <CapLabel key={suggestionKey} type="label21" className={ctaClass}>
71
+ <CapLabel key={suggestionKey} type="label21" className="rcs-cta-preview">
79
72
  <CapIcon type="small-link" size="xs" />
80
73
  {text}
81
74
  </CapLabel>
82
75
  );
83
76
  } else if (type === RCS_BUTTON_TYPES.CTA) {
84
77
  renderArray.push(
85
- <CapLabel key={suggestionKey} type="label21" className={ctaClass}>
78
+ <CapLabel key={suggestionKey} type="label21" className="rcs-cta-preview">
86
79
  <CapIcon type="launch" size="xs" />
87
80
  {text}
88
81
  </CapLabel>
89
82
  );
90
83
  } else if (type === RCS_BUTTON_TYPES.PHONE_NUMBER) {
91
- // Carousel: label text then phone icon (matches device-style RCS preview)
92
84
  renderArray.push(
93
- <CapLabel key={suggestionKey} type="label21" className={ctaClass}>
94
- {isCarousel ? (
95
- <>
96
- {text}
97
- <CapIcon type="call" size="xs" className="rcs-cta-preview-phone-icon-end" />
98
- </>
99
- ) : (
100
- <>
101
- <CapIcon type="call" size="xs" />
102
- {text}
103
- </>
104
- )}
85
+ <CapLabel key={suggestionKey} type="label21" className="rcs-cta-preview">
86
+ <CapIcon type="call" size="xs" />
87
+ {text}
105
88
  </CapLabel>
106
89
  );
107
90
  }
@@ -110,108 +93,6 @@ const RcsPreviewContent = ({
110
93
  return renderArray?.length > 0 ? renderArray : null;
111
94
  };
112
95
 
113
- // Carousel media box: aspect matches RCS editor card size (SHORT/MEDIUM × SMALL/MEDIUM).
114
- const getCarouselMediaAspectStyle = (isVideo) => {
115
- const d = content?.carouselPreviewDimensions;
116
- if (!d) return undefined;
117
- if (isVideo) {
118
- const { videoThumbWidth, videoThumbHeight } = d;
119
- if (!videoThumbWidth || !videoThumbHeight) return undefined;
120
- return { aspectRatio: `${videoThumbWidth} / ${videoThumbHeight}` };
121
- }
122
- const { imageWidth, imageHeight } = d;
123
- if (!imageWidth || !imageHeight) return undefined;
124
- return { aspectRatio: `${imageWidth} / ${imageHeight}` };
125
- };
126
-
127
- // Carousel preview (WhatsApp-style static horizontal scroll).
128
- // Caller passes a non-empty array only (see hasCarousel); avoids duplicate guards that Sonar
129
- // flagged as partially covered because inner checks were unreachable when hasCarousel was true.
130
- const renderCarouselPreviewContent = (carouselCards) => (
131
- <CapRow className="msg-container-carousel">
132
- <CapRow className="scroll-container" type="flex" noWrap>
133
- {carouselCards.map((card, idx) => {
134
- const key = `rcs-carousel-${idx}-${card?.bodyText || card?.imageSrc || card?.videoPreviewImg || ''}`;
135
- const isVideo =
136
- (card?.mediaType || '').toLowerCase() === String(MEDIA_TYPE_VIDEO).toLowerCase();
137
- const cardSuggestions = Array.isArray(card?.suggestions) ? card.suggestions : [];
138
- const suggestionsNode = cardSuggestions.length > 0
139
- ? renderRcsSuggestionsPreview(cardSuggestions, { isCarousel: true })
140
- : null;
141
- const titleTrimmed = String(card?.title ?? '').trim();
142
- const bodyTrimmed = String(card?.bodyText ?? '').trim();
143
- const titleShown = titleTrimmed || formatMessage(messages.rcsCarouselPreviewTitlePlaceholder);
144
- const bodyShown = bodyTrimmed || formatMessage(messages.rcsCarouselPreviewBodyPlaceholder);
145
- return (
146
- <CapRow
147
- key={key}
148
- className="message-pop align-left message-pop-carousel"
149
- >
150
- <CapRow className="whatsapp-content">
151
- {!isVideo && (
152
- <CapRow
153
- className="whatsapp-image rcs-carousel-media-wrap"
154
- style={getCarouselMediaAspectStyle(false)}
155
- >
156
- <CapImage
157
- src={card?.imageSrc ? card.imageSrc : rcsImageEmptyPreview}
158
- className="rcs-carousel-img"
159
- alt={formatMessage(messages.previewGenerated)}
160
- />
161
- </CapRow>
162
- )}
163
- {isVideo && (
164
- <CapTooltip
165
- title={formatMessage(messages.videoPreviewTooltip)}
166
- wrapperClassName="rcs-carousel-video-tooltip-wrapper"
167
- triggerClassName="rcs-carousel-video-tooltip-trigger"
168
- >
169
- <CapRow className="video-preview">
170
- <CapRow
171
- className="whatsapp-image rcs-carousel-media-wrap"
172
- style={getCarouselMediaAspectStyle(true)}
173
- >
174
- <CapImage
175
- src={card?.videoPreviewImg ? card.videoPreviewImg : rcsVideoEmptyPreview}
176
- className="rcs-carousel-img"
177
- alt={formatMessage(messages.previewGenerated)}
178
- />
179
- </CapRow>
180
- <CapRow className="icon-position">
181
- <CapImage className="video-icon" src={videoPlay} alt="Play" />
182
- </CapRow>
183
- </CapRow>
184
- </CapTooltip>
185
- )}
186
-
187
- <CapRow className="carousel-content">
188
- <CapLabel
189
- type={card.titleLabelType || 'label1'}
190
- className={`carousel-title${titleTrimmed ? '' : ' rcs-carousel-field-placeholder'}`}
191
- >
192
- {titleShown}
193
- </CapLabel>
194
- <CapLabel
195
- type={card.bodyLabelType || 'label2'}
196
- className={`carousel-message${bodyTrimmed ? '' : ' rcs-carousel-field-placeholder'}`}
197
- >
198
- {bodyShown}
199
- </CapLabel>
200
- </CapRow>
201
-
202
- {!!suggestionsNode && (
203
- <CapRow className="rcs-carousel-cta-stack" gutter={0}>
204
- {suggestionsNode}
205
- </CapRow>
206
- )}
207
- </CapRow>
208
- </CapRow>
209
- );
210
- })}
211
- </CapRow>
212
- </CapRow>
213
- );
214
-
215
96
  // Render text preview content
216
97
  const renderTextPreviewContent = () => {
217
98
  const {
@@ -345,39 +226,11 @@ const RcsPreviewContent = ({
345
226
  const timestamp = `${displayHours}:${displayMinutes} ${ampm}`;
346
227
 
347
228
  // Render normal RCS preview (same structure as SMS up to sms-message-container)
348
- const carouselCardsNormalized = Array.isArray(content?.carouselData) ? content?.carouselData : [];
349
- const hasCarousel = carouselCardsNormalized?.length > 0;
350
229
  const hasMedia = !!(content?.rcsImageSrc || content?.rcsVideoSrc);
351
- let contentToRender;
352
- if (hasCarousel) {
353
- contentToRender = renderCarouselPreviewContent(carouselCardsNormalized);
354
- } else if (hasMedia) {
355
- contentToRender = renderMediaPreviewContent();
356
- } else {
357
- contentToRender = renderTextPreviewContent();
358
- }
359
-
360
- const carouselPanelStyle = hasCarousel ? {
361
- position: 'absolute',
362
- left: '50%',
363
- transform: 'translateX(-50%)',
364
- width: '100%',
365
- maxWidth: '26.786rem',
366
- boxSizing: 'border-box',
367
- top: device === ANDROID ? '3.1rem' : '4.143rem',
368
- height: device === ANDROID ? 'calc(100% - 3.8rem)' : device === IOS ? 'calc(100% - 5rem)' : 'calc(100% - 4.5rem)',
369
- display: 'flex',
370
- flexDirection: 'column',
371
- paddingTop: '5.857rem',
372
- paddingBottom: '1.142rem',
373
- } : undefined;
230
+ const contentToRender = hasMedia ? renderMediaPreviewContent() : renderTextPreviewContent();
374
231
 
375
232
  return (
376
- <CapRow
377
- type='flex' align='middle' justify='center'
378
- className={`sms-device-container rcs-device-container ${hasCarousel ? 'rcs-device-container-carousel' : ''}`}
379
- style={{ position: 'relative' }}
380
- >
233
+ <CapRow type='flex' align='middle' justify='center' className="sms-device-container">
381
234
  {/* Device Background Image */}
382
235
  <CapImage
383
236
  className="sms-device-image"
@@ -385,10 +238,8 @@ const RcsPreviewContent = ({
385
238
  alt={`${deviceName} Device`}
386
239
  />
387
240
 
388
- {/* Content Overlay - nav bar + date separator always; message content only for non-carousel */}
389
- <CapRow
390
- className={`sms-content-overlay sms-content-overlay-${device} sms-preview sms-${device} rcs-content-overlay`}
391
- >
241
+ {/* Content Overlay */}
242
+ <CapRow className={`sms-content-overlay sms-content-overlay-${device} sms-preview sms-${device}`}>
392
243
  {/* Navigation Bar - Same as SMS */}
393
244
  <CapRow type='flex' className={`sms-navigation-bar ${!showHeader ? 'sms-navigation-bar-no-header' : ''}`}>
394
245
  <CapIcon type="chevron-left" className="sms-back-arrow" />
@@ -403,41 +254,24 @@ const RcsPreviewContent = ({
403
254
  <FormattedMessage {...messages.timeAgo} defaultMessage="Today" />
404
255
  </CapRow>
405
256
 
406
- {/* Message Container - non-carousel only; carousel is rendered outside this overflow-x:hidden ancestor */}
407
- {!hasCarousel && (
408
- <CapRow className="sms-message-container">
409
- <CapRow useLegacy className="sms-message-bubble">
410
- <CapRow useLegacy className="sms-message-text">
411
- <CapRow useLegacy className="message-pop sms">
412
- {contentToRender}
413
- </CapRow>
257
+ {/* Message Container - Same structure as SMS, only content inside is RCS-specific */}
258
+ <CapRow className="sms-message-container">
259
+ <CapRow useLegacy className="sms-message-bubble">
260
+ <CapRow useLegacy className="sms-message-text">
261
+ {/* RCS-specific content rendering */}
262
+ <CapRow useLegacy className="message-pop sms">
263
+ {contentToRender}
414
264
  </CapRow>
415
265
  </CapRow>
416
- <CapRow useLegacy className="sms-timestamp">
417
- {timestamp}
418
- </CapRow>
419
266
  </CapRow>
420
- )}
421
- </CapRow>
422
-
423
- {/* Carousel panel: sibling of overlay, no overflow-x:hidden ancestor → scroll-container
424
- can receive horizontal scroll events. Positioned absolutely over the device image. */}
425
- {hasCarousel && (
426
- <CapRow
427
- className={`rcs-carousel-panel rcs-carousel-panel-${device}`}
428
- style={carouselPanelStyle}
429
- >
430
- {renderCarouselPreviewContent(carouselCardsNormalized)}
431
- <CapRow useLegacy className="sms-timestamp rcs-carousel-timestamp">
267
+ <CapRow useLegacy className="sms-timestamp">
432
268
  {timestamp}
433
269
  </CapRow>
434
270
  </CapRow>
435
- )}
271
+ </CapRow>
436
272
  </CapRow>
437
- )
438
- }
439
-
440
-
273
+ );
274
+ };
441
275
 
442
276
  RcsPreviewContent.propTypes = {
443
277
  content: PropTypes.shape({
@@ -448,18 +282,6 @@ RcsPreviewContent.propTypes = {
448
282
  rcsImageSrc: PropTypes.string,
449
283
  rcsVideoSrc: PropTypes.string,
450
284
  rcsThumbnailSrc: PropTypes.string,
451
- carouselData: PropTypes.arrayOf(
452
- PropTypes.shape({
453
- titleLabelType: PropTypes.string,
454
- bodyLabelType: PropTypes.string,
455
- })
456
- ),
457
- carouselPreviewDimensions: PropTypes.shape({
458
- imageWidth: PropTypes.number,
459
- imageHeight: PropTypes.number,
460
- videoThumbWidth: PropTypes.number,
461
- videoThumbHeight: PropTypes.number,
462
- }),
463
285
  suggestions: PropTypes.arrayOf(
464
286
  PropTypes.shape({
465
287
  type: PropTypes.string,