@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
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * DeliverySettings — summary row + CapSlideBox trigger for Test and Preview.
3
- * Rendered only for SMS, Email, WhatsApp. Uses cap-ui-library only.
3
+ * Rendered for SMS, Email, WhatsApp, RCS (ModifyDeliverySettings). RCS uses gsmSenderId + cdmaSenderId (SMS fallback).
4
4
  *
5
5
  * Reference: cap-campaigns-v2 DeliverySettingsV2 (summary + modify panel).
6
6
  */
7
7
 
8
- import React, { useState, useCallback } from "react";
8
+ import React, { useState, useCallback, useMemo } from "react";
9
9
  import PropTypes from "prop-types";
10
10
  import CapRow from "@capillarytech/cap-ui-library/CapRow";
11
11
  import CapHeading from "@capillarytech/cap-ui-library/CapHeading";
@@ -22,6 +22,7 @@ import {
22
22
  SUMMARY_EMPTY_PLACEHOLDER,
23
23
  DELIVERY_SETTING_KEY_DOMAIN_ID,
24
24
  DELIVERY_SETTING_KEY_GSM_SENDER_ID,
25
+ DELIVERY_SETTING_KEY_CDMA_SENDER_ID,
25
26
  DELIVERY_SETTING_KEY_SENDER_EMAIL,
26
27
  DELIVERY_SETTING_KEY_SENDER_LABEL,
27
28
  DELIVERY_SETTING_KEY_SENDER_REPLY_TO,
@@ -35,7 +36,7 @@ const DeliverySettings = (props) => {
35
36
  const {
36
37
  channel,
37
38
  deliverySettings = {},
38
- senderDetailsOptions = [],
39
+ senderDetailsByChannel = {},
39
40
  wecrmAccounts = [],
40
41
  onSaveDeliverySettings,
41
42
  isLoadingSenderDetails,
@@ -43,6 +44,7 @@ const DeliverySettings = (props) => {
43
44
  smsTraiDltEnabled,
44
45
  registeredSenderIds,
45
46
  whatsappAccountFromForm,
47
+ isChannelSmsFallbackPreviewEnabled,
46
48
  } = props;
47
49
 
48
50
  const [isSlideBoxOpen, setSlideBoxOpen] = useState(false);
@@ -58,17 +60,68 @@ const DeliverySettings = (props) => {
58
60
  [onSaveDeliverySettings, closeSlideBox]
59
61
  );
60
62
 
63
+ const senderDetailsOptions = useMemo(
64
+ () => (senderDetailsByChannel[channel] || []),
65
+ [senderDetailsByChannel, channel],
66
+ );
67
+ const smsDomainsForRcs = useMemo(
68
+ () => (channel === CHANNELS.RCS ? (senderDetailsByChannel[CHANNELS.SMS] || []) : []),
69
+ [channel, senderDetailsByChannel],
70
+ );
71
+
61
72
  if (!CHANNELS_WITH_DELIVERY_SETTINGS.includes(channel)) {
62
73
  return null;
63
74
  }
64
75
 
65
76
  const notConfiguredLabel = formatMessage(messages.notConfigured);
66
77
 
78
+ const getDisplayFromDomains = (domains, compositeValue) => {
79
+ if (!compositeValue || !domains?.length) return compositeValue || SUMMARY_EMPTY_PLACEHOLDER;
80
+ for (const senderDomain of domains) {
81
+ for (const gsmSenderRow of senderDomain.gsmSenders || []) {
82
+ const value = senderDomain.domainId != null && gsmSenderRow.value != null
83
+ ? `${senderDomain.domainId}|${gsmSenderRow.value}`
84
+ : gsmSenderRow.value;
85
+ if (value === compositeValue && (senderDomain.domainName || gsmSenderRow.value)) {
86
+ return [senderDomain.domainName, gsmSenderRow.value].filter(Boolean).join(' · ');
87
+ }
88
+ }
89
+ }
90
+ return compositeValue || SUMMARY_EMPTY_PLACEHOLDER;
91
+ };
92
+
93
+ /** RCS summary: show sender ID only (no "domain · sender"). */
94
+ const getSenderIdOnlyFromComposite = (domains, compositeValue) => {
95
+ if (!compositeValue || !domains?.length) return compositeValue || SUMMARY_EMPTY_PLACEHOLDER;
96
+ for (const senderDomain of domains) {
97
+ for (const gsmSenderRow of senderDomain.gsmSenders || []) {
98
+ const value = senderDomain.domainId != null && gsmSenderRow.value != null
99
+ ? `${senderDomain.domainId}|${gsmSenderRow.value}`
100
+ : gsmSenderRow.value;
101
+ if (value === compositeValue && gsmSenderRow.value != null) {
102
+ return String(gsmSenderRow.value);
103
+ }
104
+ }
105
+ }
106
+ return compositeValue || SUMMARY_EMPTY_PLACEHOLDER;
107
+ };
108
+
67
109
  const getSummaryText = () => {
68
110
  const empty = SUMMARY_EMPTY_PLACEHOLDER;
111
+ if (channel === CHANNELS.RCS) {
112
+ const rcsVal = get(deliverySettings, DELIVERY_SETTING_KEY_GSM_SENDER_ID) || '';
113
+ const smsVal = get(deliverySettings, DELIVERY_SETTING_KEY_CDMA_SENDER_ID) || '';
114
+ const summaryRcs = {
115
+ rcsSenderId: getSenderIdOnlyFromComposite(senderDetailsOptions, rcsVal),
116
+ };
117
+ if (isChannelSmsFallbackPreviewEnabled) {
118
+ summaryRcs.smsFallbackSenderId = getSenderIdOnlyFromComposite(smsDomainsForRcs, smsVal);
119
+ }
120
+ return summaryRcs;
121
+ }
69
122
  if (channel === CHANNELS.SMS) {
70
123
  const domain = (senderDetailsOptions || []).find(
71
- (d) => d.domainId === get(deliverySettings, DELIVERY_SETTING_KEY_DOMAIN_ID),
124
+ (senderDomain) => senderDomain.domainId === get(deliverySettings, DELIVERY_SETTING_KEY_DOMAIN_ID),
72
125
  );
73
126
  const domainName = domain?.domainName || empty;
74
127
  const senderId = get(deliverySettings, DELIVERY_SETTING_KEY_GSM_SENDER_ID) || empty;
@@ -76,7 +129,7 @@ const DeliverySettings = (props) => {
76
129
  }
77
130
  if (channel === CHANNELS.EMAIL) {
78
131
  const domain = (senderDetailsOptions || []).find(
79
- (d) => d.domainId === get(deliverySettings, DELIVERY_SETTING_KEY_DOMAIN_ID),
132
+ (senderDomain) => senderDomain.domainId === get(deliverySettings, DELIVERY_SETTING_KEY_DOMAIN_ID),
80
133
  );
81
134
  const emailDomain = domain?.domainName || empty;
82
135
  const senderEmail = get(deliverySettings, DELIVERY_SETTING_KEY_SENDER_EMAIL) || empty;
@@ -103,7 +156,12 @@ const DeliverySettings = (props) => {
103
156
 
104
157
  const summary = getSummaryText();
105
158
  let hasValues = false;
106
- if (channel === CHANNELS.SMS) {
159
+ if (channel === CHANNELS.RCS) {
160
+ hasValues = (summary.rcsSenderId && summary.rcsSenderId !== SUMMARY_EMPTY_PLACEHOLDER)
161
+ || (isChannelSmsFallbackPreviewEnabled
162
+ && summary.smsFallbackSenderId
163
+ && summary.smsFallbackSenderId !== SUMMARY_EMPTY_PLACEHOLDER);
164
+ } else if (channel === CHANNELS.SMS) {
107
165
  hasValues = summary.senderDomain !== SUMMARY_EMPTY_PLACEHOLDER
108
166
  || summary.senderId !== SUMMARY_EMPTY_PLACEHOLDER;
109
167
  } else if (channel === CHANNELS.EMAIL) {
@@ -122,6 +180,15 @@ const DeliverySettings = (props) => {
122
180
  const getSummaryObject = () => {
123
181
  if (!hasValues) return null;
124
182
  const label = (msg) => formatMessage(msg);
183
+ if (channel === CHANNELS.RCS) {
184
+ const rcsSummary = {
185
+ [label(messages.rcsSenderIdLabel)]: summary.rcsSenderId,
186
+ };
187
+ if (isChannelSmsFallbackPreviewEnabled) {
188
+ rcsSummary[label(messages.fallbackSmsSenderIdLabel)] = summary.smsFallbackSenderId;
189
+ }
190
+ return rcsSummary;
191
+ }
125
192
  if (channel === CHANNELS.SMS) {
126
193
  return {
127
194
  [label(messages.senderDomainSummary)]: summary.senderDomain,
@@ -165,7 +232,7 @@ const DeliverySettings = (props) => {
165
232
  Object.entries(summaryObject).map(([key, value]) => (
166
233
  <CapRow type="flex" key={key} className="delivery-settings__summary-entry">
167
234
  <CapLabel type="label1" className="delivery-settings__summary-key">{key}</CapLabel>
168
- <CapLabel type="label9">
235
+ <CapLabel type="label9" className="delivery-settings__summary-value">
169
236
  :
170
237
  {' '}
171
238
  {value}
@@ -194,6 +261,11 @@ const DeliverySettings = (props) => {
194
261
  channel={channel}
195
262
  deliverySettings={deliverySettings}
196
263
  senderDetailsOptions={senderDetailsOptions}
264
+ smsFallbackSenderDetailsOptions={
265
+ channel === CHANNELS.RCS && isChannelSmsFallbackPreviewEnabled
266
+ ? smsDomainsForRcs
267
+ : undefined
268
+ }
197
269
  wecrmAccounts={wecrmAccounts}
198
270
  onSaveDeliverySettings={handleSave}
199
271
  onClose={closeSlideBox}
@@ -213,7 +285,7 @@ const DeliverySettings = (props) => {
213
285
  DeliverySettings.propTypes = {
214
286
  channel: PropTypes.string.isRequired,
215
287
  deliverySettings: PropTypes.object,
216
- senderDetailsOptions: PropTypes.array,
288
+ senderDetailsByChannel: PropTypes.object,
217
289
  wecrmAccounts: PropTypes.array,
218
290
  onSaveDeliverySettings: PropTypes.func.isRequired,
219
291
  isLoadingSenderDetails: PropTypes.bool,
@@ -223,17 +295,19 @@ DeliverySettings.propTypes = {
223
295
  whatsappAccountFromForm: PropTypes.shape({
224
296
  accountName: PropTypes.string,
225
297
  }),
298
+ isChannelSmsFallbackPreviewEnabled: PropTypes.bool,
226
299
  };
227
300
 
228
301
  DeliverySettings.defaultProps = {
229
302
  deliverySettings: {},
230
- senderDetailsOptions: [],
303
+ senderDetailsByChannel: {},
231
304
  wecrmAccounts: [],
232
305
  isLoadingSenderDetails: false,
233
306
  formatMessage: undefined,
234
307
  smsTraiDltEnabled: false,
235
308
  registeredSenderIds: [],
236
309
  whatsappAccountFromForm: undefined,
310
+ isChannelSmsFallbackPreviewEnabled: false,
237
311
  };
238
312
 
239
313
  export default DeliverySettings;
@@ -108,4 +108,34 @@ export default defineMessages({
108
108
  id: `${prefix}.disabledMessage`,
109
109
  defaultMessage: 'Message template selected belongs to this account',
110
110
  },
111
+ /** RCS: primary domain (slidebox) */
112
+ rcsDomainLabel: {
113
+ id: `${prefix}.rcsDomainLabel`,
114
+ defaultMessage: 'RCS domain:',
115
+ },
116
+ /** RCS: SMS fallback domain (slidebox) */
117
+ fallbackSmsDomainLabel: {
118
+ id: `${prefix}.fallbackSmsDomainLabel`,
119
+ defaultMessage: 'Fallback SMS domain:',
120
+ },
121
+ /** RCS: "RCS sender ID" */
122
+ rcsSenderIdLabel: {
123
+ id: `${prefix}.rcsSenderIdLabel`,
124
+ defaultMessage: 'RCS sender ID',
125
+ },
126
+ /** RCS: "Fallback SMS sender ID" */
127
+ fallbackSmsSenderIdLabel: {
128
+ id: `${prefix}.fallbackSmsSenderIdLabel`,
129
+ defaultMessage: 'Fallback SMS sender ID',
130
+ },
131
+ /** RCS: "Domain" (for domain · sender ID display) */
132
+ domainLabel: {
133
+ id: `${prefix}.domainLabel`,
134
+ defaultMessage: 'Domain',
135
+ },
136
+ /** RCS: placeholder for sender ID dropdown */
137
+ selectSenderId: {
138
+ id: `${prefix}.selectSenderId`,
139
+ defaultMessage: 'Select sender ID',
140
+ },
111
141
  });
@@ -9,29 +9,70 @@
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';
12
14
 
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
+ */
13
19
  const getSenderOptions = (contactInfo, key) =>
14
20
  (contactInfo || [])
15
- .filter((contactInfoItem) => contactInfoItem.type === key && contactInfoItem.valid)
21
+ .filter((contactInfoItem) => typeMatches(contactInfoItem.type, key) && contactInfoItem.valid !== false)
16
22
  .sort((contact) => (contact.default ? -1 : 0));
17
23
 
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
+
18
40
  /**
19
41
  * Parse raw API response for one channel into { domains: [...] }
20
- * @param {string} channel - SMS | EMAIL | WHATSAPP
42
+ * @param {string} channel - SMS | EMAIL | WHATSAPP | RCS
21
43
  * @param {Object} response - API response (e.g. { entity: { SMS: [...] } } or { entity: [...] })
22
44
  * @returns {{ domains: Array }} - domains array compatible with campaigns DeliverySettingsV2
23
45
  */
24
46
  export function parseSenderDetailsResponse(channel, response) {
25
- const entity = get(response, 'entity', response);
26
- if (!entity) {
47
+ const entity = unwrapEntity(response);
48
+ if (entity == null || entity === '') {
27
49
  return { domains: [] };
28
50
  }
29
51
 
30
- // Single-channel API: entity may be { [channel]: [...] } or direct array
31
- let channelSenderDetails = get(entity, channel, null);
52
+ // Normalize once: entity keys may be any casing (SMS/sms/rcs) but downstream branches use CHANNELS.* (uppercase).
53
+ const trimmedChannel = channel == null ? '' : String(channel).trim();
54
+ const normalizedChannel = trimmedChannel ? trimmedChannel.toUpperCase() : '';
55
+
56
+ // Single-channel API: entity may be { [channel]: [...] } or direct array.
57
+ // Some responses use different casing (e.g. rcs vs RCS) or a single domain object instead of an array.
58
+ let channelSenderDetails =
59
+ get(entity, channel, null)
60
+ ?? (normalizedChannel ? get(entity, normalizedChannel, null) : null)
61
+ ?? (normalizedChannel ? get(entity, normalizedChannel.toLowerCase(), null) : null);
62
+
32
63
  if (channelSenderDetails == null && Array.isArray(entity)) {
33
64
  channelSenderDetails = entity;
34
65
  }
66
+
67
+ if (channelSenderDetails != null && !Array.isArray(channelSenderDetails)) {
68
+ const single = channelSenderDetails;
69
+ if (single && typeof single === 'object' && single.domainProperties) {
70
+ channelSenderDetails = [single];
71
+ } else {
72
+ return { domains: [] };
73
+ }
74
+ }
75
+
35
76
  if (!Array.isArray(channelSenderDetails)) {
36
77
  return { domains: [] };
37
78
  }
@@ -42,6 +83,7 @@ export function parseSenderDetailsResponse(channel, response) {
42
83
  const { id: dgmId, priority, domainProperties = {} } = element;
43
84
  const {
44
85
  domainName,
86
+ hostName,
45
87
  id = -1,
46
88
  contactInfo,
47
89
  connectionProperties: {
@@ -51,21 +93,25 @@ export function parseSenderDetailsResponse(channel, response) {
51
93
  } = {},
52
94
  } = domainProperties;
53
95
 
96
+ const resolvedDomainName = domainName || hostName;
97
+
54
98
  const domain = {
55
99
  dgmId,
56
- domainName,
100
+ domainName: resolvedDomainName,
57
101
  domainId: id,
58
102
  priority: priority != null ? priority : 0,
59
103
  };
60
104
 
61
- if ([CHANNELS.SMS, CHANNELS.WHATSAPP].includes(channel)) {
105
+ if ([CHANNELS.SMS, CHANNELS.WHATSAPP, CHANNELS.RCS].includes(normalizedChannel)) {
62
106
  domain.cdmaSenders = getSenderOptions(contactInfo, 'cdma_sender_id');
63
- domain.gsmSenders = getSenderOptions(contactInfo, 'gsm_sender_id');
107
+ domain.gsmSenders = normalizedChannel === CHANNELS.RCS
108
+ ? getRcsLikeGsmSenders(contactInfo)
109
+ : getSenderOptions(contactInfo, 'gsm_sender_id');
64
110
  domain.sourceAccountIdentifier =
65
111
  sourceAccountIdentifier || wabaId || userid || '';
66
112
  }
67
113
 
68
- if (channel === CHANNELS.EMAIL) {
114
+ if (normalizedChannel === CHANNELS.EMAIL) {
69
115
  domain.emailSenders = getSenderOptions(contactInfo, 'sender_id');
70
116
  domain.emailRepliers = getSenderOptions(contactInfo, 'reply_to_id');
71
117
  }
@@ -76,7 +122,8 @@ export function parseSenderDetailsResponse(channel, response) {
76
122
  // Sort by priority and dedupe by domainName (match campaigns behaviour)
77
123
  domains.sort((a, b) => (a.priority || 0) - (b.priority || 0));
78
124
  const deduped = domains.reduce((acc, domain) => {
79
- if (!acc.find((d) => d.domainName === domain.domainName)) {
125
+ const key = domain.domainName ?? domain.domainId;
126
+ if (!acc.find((existing) => (existing.domainName ?? existing.domainId) === key)) {
80
127
  acc.push(domain);
81
128
  }
82
129
  return acc;
@@ -6,12 +6,13 @@ 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';
9
10
  import isEmpty from 'lodash/isEmpty';
10
11
  import messages from './messages';
11
12
  import DeliverySettings from './DeliverySettings';
12
13
  import { CHANNELS } from './constants';
13
14
 
14
- const CHANNELS_WITH_DELIVERY_SETTINGS = [CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP];
15
+ const CHANNELS_WITH_DELIVERY_SETTINGS = [CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP, CHANNELS.RCS];
15
16
 
16
17
  const SendTestMessage = ({
17
18
  isFetchingTestCustomers,
@@ -28,12 +29,13 @@ const SendTestMessage = ({
28
29
  searchValue,
29
30
  setSearchValue,
30
31
  deliverySettings,
31
- senderDetailsOptions,
32
+ senderDetailsByChannel = {},
32
33
  wecrmAccounts,
33
34
  onSaveDeliverySettings,
34
35
  isLoadingSenderDetails,
35
36
  smsTraiDltEnabled,
36
37
  registeredSenderIds,
38
+ isChannelSmsFallbackPreviewEnabled,
37
39
  }) => {
38
40
  const addCustomerContent = renderAddTestCustomerButton ? renderAddTestCustomerButton() : null;
39
41
  return (
@@ -77,13 +79,14 @@ const SendTestMessage = ({
77
79
  <DeliverySettings
78
80
  channel={channel}
79
81
  deliverySettings={deliverySettings || {}}
80
- senderDetailsOptions={senderDetailsOptions}
82
+ senderDetailsByChannel={senderDetailsByChannel}
81
83
  wecrmAccounts={wecrmAccounts || []}
82
84
  onSaveDeliverySettings={onSaveDeliverySettings}
83
85
  isLoadingSenderDetails={isLoadingSenderDetails}
84
86
  formatMessage={formatMessage}
85
87
  smsTraiDltEnabled={smsTraiDltEnabled}
86
88
  registeredSenderIds={registeredSenderIds}
89
+ isChannelSmsFallbackPreviewEnabled={isChannelSmsFallbackPreviewEnabled}
87
90
  whatsappAccountFromForm={
88
91
  channel === CHANNELS.WHATSAPP && formData?.accountName
89
92
  ? { accountName: formData.accountName }
@@ -121,12 +124,13 @@ SendTestMessage.propTypes = {
121
124
  searchValue: PropTypes.string,
122
125
  setSearchValue: PropTypes.func,
123
126
  deliverySettings: PropTypes.object,
124
- senderDetailsOptions: PropTypes.array,
127
+ senderDetailsByChannel: PropTypes.object,
125
128
  wecrmAccounts: PropTypes.array,
126
129
  onSaveDeliverySettings: PropTypes.func,
127
130
  isLoadingSenderDetails: PropTypes.bool,
128
131
  smsTraiDltEnabled: PropTypes.bool,
129
132
  registeredSenderIds: PropTypes.array,
133
+ isChannelSmsFallbackPreviewEnabled: PropTypes.bool,
130
134
  };
131
135
 
132
136
  SendTestMessage.defaultProps = {
@@ -136,12 +140,13 @@ SendTestMessage.defaultProps = {
136
140
  searchValue: '',
137
141
  setSearchValue: () => {}, // no-op when not provided; required by TreeSelect when showSearch is true
138
142
  deliverySettings: {},
139
- senderDetailsOptions: [],
143
+ senderDetailsByChannel: {},
140
144
  wecrmAccounts: [],
141
145
  onSaveDeliverySettings: undefined,
142
146
  isLoadingSenderDetails: false,
143
147
  smsTraiDltEnabled: false,
144
148
  registeredSenderIds: [],
149
+ isChannelSmsFallbackPreviewEnabled: false,
145
150
  };
146
151
 
147
152
  export default SendTestMessage;