@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,8 @@ import CapButton from '@capillarytech/cap-ui-library/CapButton';
8
8
  import CapInput from '@capillarytech/cap-ui-library/CapInput';
9
9
  import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
10
10
  import messages from './messages';
11
+ import { CUSTOM_VALUES_EDITOR_SECTION_FALLBACK_KEY } from './constants';
12
+ import { getPersonalizationTagColumnLabel } from './utils';
11
13
 
12
14
  const CustomValuesEditor = ({
13
15
  isExtractingTags,
@@ -16,9 +18,7 @@ const CustomValuesEditor = ({
16
18
  setShowJSON,
17
19
  customValues,
18
20
  handleJSONTextChange,
19
- extractedTags,
20
- requiredTags,
21
- optionalTags,
21
+ sections,
22
22
  handleCustomValueChange,
23
23
  handleDiscardCustomValues,
24
24
  handleUpdatePreview,
@@ -77,51 +77,70 @@ const CustomValuesEditor = ({
77
77
  </CapRow>
78
78
  ) : (
79
79
  <>
80
- {extractedTags?.length > 0 && (
81
- <CapRow className="values-table">
82
- <CapRow className="table-header" align='middle' justify='space-between'>
83
- <CapLabel type="label31" className="header-cell">
84
- <FormattedMessage {...messages.personalizationTags} />
80
+ {(sections || []).filter((tagsSection) =>
81
+ (tagsSection?.requiredTags?.length || 0) + (tagsSection?.optionalTags?.length || 0) > 0).map((section) => (
82
+ <React.Fragment key={section.key || section.title?.id || section.title || CUSTOM_VALUES_EDITOR_SECTION_FALLBACK_KEY}>
83
+ {section.title ? (
84
+ <CapLabel type="label2" className="tags-section-title">
85
+ {typeof section.title === 'string' ? section.title : <FormattedMessage {...section.title} />}
85
86
  </CapLabel>
86
- <CapLabel type="label31" className="header-cell">
87
- <FormattedMessage {...messages.customValues} />
88
- </CapLabel>
89
- </CapRow>
90
- {requiredTags.map((tag) => (
91
- <CapRow key={tag.fullPath} className="value-row" align='middle' justify='start'>
92
- <CapRow className="tag-name" align='top' justify='start'>
93
- {`${tag.fullPath}*`}
94
- </CapRow>
95
- <CapRow className="tag-input">
96
- <CapInput
97
- type="text"
98
- isRequired
99
- className="tag-input-field"
100
- value={customValues[tag.fullPath] || ''}
101
- onChange={(e) => handleCustomValueChange(tag.fullPath, e.target.value)}
102
- placeholder={formatMessage(messages.enterValue)}
103
- size="small"
104
- />
105
- </CapRow>
87
+ ) : null}
88
+ <CapRow className="values-table">
89
+ <CapRow type="flex" className="table-header">
90
+ <CapLabel type="label31" className="header-cell">
91
+ <FormattedMessage {...messages.personalizationTags} />
92
+ </CapLabel>
93
+ <CapLabel type="label31" className="header-cell">
94
+ <FormattedMessage {...messages.customValues} />
95
+ </CapLabel>
106
96
  </CapRow>
107
- ))}
108
- {optionalTags?.map((tag) => (
109
- <CapRow key={tag.fullPath} className="value-row" align='middle' justify='start'>
110
- <CapRow className="tag-name" justify='start'>{tag.fullPath}</CapRow>
111
- <CapRow className="tag-input">
112
- <CapInput
113
- type="text"
114
- className="tag-input-field"
115
- value={customValues[tag.fullPath] || ''}
116
- onChange={(e) => handleCustomValueChange(tag.fullPath, e.target.value)}
117
- placeholder={formatMessage(messages.enterValue)}
118
- size="small"
119
- />
97
+ {(section?.requiredTags || []).map((tag, tagIndex) => {
98
+ const personalizationTagColumnText = getPersonalizationTagColumnLabel(tag);
99
+ const tagKey = tag?.fullPath ?? `required-${tagIndex}`;
100
+ return (
101
+ <CapRow key={tagKey} type="flex" noWrap className="value-row">
102
+ <CapRow type="flex" className="tag-name">
103
+ {personalizationTagColumnText}
104
+ <span className="required-tag-indicator" aria-hidden="true">*</span>
105
+ </CapRow>
106
+ <CapRow type="flex" className="tag-input">
107
+ <CapInput
108
+ type="text"
109
+ isRequired
110
+ className="tag-input-field"
111
+ value={customValues?.[tagKey] ?? ''}
112
+ onChange={(e) => handleCustomValueChange(tagKey, e.target.value)}
113
+ placeholder={formatMessage(messages.enterValue)}
114
+ size="small"
115
+ />
116
+ </CapRow>
120
117
  </CapRow>
121
- </CapRow>
122
- ))}
123
- </CapRow>
124
- )}
118
+ );
119
+ })}
120
+ {(section?.optionalTags || []).map((tag, tagIndex) => {
121
+ const personalizationTagColumnText = getPersonalizationTagColumnLabel(tag);
122
+ const tagKey = tag?.fullPath ?? `optional-${tagIndex}`;
123
+ return (
124
+ <CapRow key={tagKey} type="flex" noWrap className="value-row">
125
+ <CapRow type="flex" className="tag-name">
126
+ {personalizationTagColumnText}
127
+ </CapRow>
128
+ <CapRow type="flex" className="tag-input">
129
+ <CapInput
130
+ type="text"
131
+ className="tag-input-field"
132
+ value={customValues?.[tagKey] ?? ''}
133
+ onChange={(e) => handleCustomValueChange(tagKey, e.target.value)}
134
+ placeholder={formatMessage(messages.enterValue)}
135
+ size="small"
136
+ />
137
+ </CapRow>
138
+ </CapRow>
139
+ );
140
+ })}
141
+ </CapRow>
142
+ </React.Fragment>
143
+ ))}
125
144
  </>
126
145
  )}
127
146
  <CapRow type='flex' align='middle' justify='space-between' className="editor-actions">
@@ -155,9 +174,12 @@ CustomValuesEditor.propTypes = {
155
174
  setShowJSON: PropTypes.func.isRequired,
156
175
  customValues: PropTypes.object.isRequired,
157
176
  handleJSONTextChange: PropTypes.func.isRequired,
158
- extractedTags: PropTypes.array.isRequired,
159
- requiredTags: PropTypes.array.isRequired,
160
- optionalTags: PropTypes.array.isRequired,
177
+ sections: PropTypes.arrayOf(PropTypes.shape({
178
+ key: PropTypes.string,
179
+ title: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
180
+ requiredTags: PropTypes.array,
181
+ optionalTags: PropTypes.array,
182
+ })).isRequired,
161
183
  handleCustomValueChange: PropTypes.func.isRequired,
162
184
  handleDiscardCustomValues: PropTypes.func.isRequired,
163
185
  handleUpdatePreview: PropTypes.func.isRequired,
@@ -18,11 +18,17 @@
18
18
  }
19
19
 
20
20
  &__summary-entry {
21
+ display: flex;
22
+ align-items: baseline;
23
+ gap: 0;
21
24
  margin-right: $CAP_SPACE_18;
22
25
  }
23
26
 
24
- &__summary-key {
25
- line-height: $CAP_SPACE_16;
27
+ &__summary-key,
28
+ &__summary-value {
29
+ line-height: 1.4;
30
+ margin-top: 0;
31
+ margin-bottom: 0;
26
32
  }
27
33
 
28
34
  &__edit-icon {
@@ -32,16 +32,37 @@ import {
32
32
  DELIVERY_SETTING_KEY_SENDER_REPLY_TO,
33
33
  DELIVERY_SETTING_KEY_SOURCE_ACCOUNT_IDENTIFIER,
34
34
  DELIVERY_SETTING_KEY_SENDER_MOB_NUM,
35
+ DELIVERY_SETTING_KEY_SMS_FALLBACK_DOMAIN_ID,
35
36
  } from './constants';
36
37
  import './ModifyDeliverySettings.scss';
37
38
 
38
39
  const findDefault = (array) => find(array, { default: true }) || (array && array[0]) || {};
39
40
 
41
+ /**
42
+ * Drop empty GSM rows. Optionally drop rows whose value equals domain name — some APIs duplicate
43
+ * domain label as a bogus sender row; RCS/DLT often legitimately use the same string (e.g. DetailsForGW1).
44
+ */
45
+ const filterUsableGsmSendersForDomain = (domain, gsmSenders, { skipDomainNameEchoFilter = false } = {}) => {
46
+ const normalizedDomainName =
47
+ domain?.domainName != null ? String(domain.domainName).trim().toLowerCase() : '';
48
+ return (gsmSenders || []).filter((gsmSenderRow) => {
49
+ const rawValue = gsmSenderRow?.value;
50
+ if (rawValue == null) return false;
51
+ const trimmedSenderValue = String(rawValue).trim();
52
+ if (!trimmedSenderValue) return false;
53
+ const senderMatchesDomainLabel =
54
+ normalizedDomainName && trimmedSenderValue.toLowerCase() === normalizedDomainName;
55
+ if (!skipDomainNameEchoFilter && senderMatchesDomainLabel) return false;
56
+ return true;
57
+ });
58
+ };
59
+
40
60
  const ModifyDeliverySettings = (props) => {
41
61
  const {
42
62
  channel,
43
63
  deliverySettings: initialSettings = {},
44
64
  senderDetailsOptions = [],
65
+ smsFallbackSenderDetailsOptions = [],
45
66
  wecrmAccounts = [],
46
67
  onSaveDeliverySettings,
47
68
  onClose,
@@ -102,17 +123,21 @@ const ModifyDeliverySettings = (props) => {
102
123
  if (!(channel === CHANNELS.SMS && smsTraiDltEnabled)) {
103
124
  return allDomainOptions;
104
125
  }
126
+ // Without template registered sender IDs, strict DLT filter removes every domain — keep full list.
127
+ if (!registeredSenderIds?.length) {
128
+ return allDomainOptions;
129
+ }
105
130
 
106
- return allDomainOptions.filter((domain) => (domain.gsmSenders || []).some(
107
- (gsmId) => registeredSenderIds.includes(gsmId.value),
131
+ return allDomainOptions.filter((domain) => (domain?.gsmSenders || []).some(
132
+ (gsmSenderOption) => registeredSenderIds?.includes(gsmSenderOption?.value),
108
133
  ));
109
134
  }, [allDomainOptions, channel, registeredSenderIds, smsTraiDltEnabled]);
110
135
 
111
136
  const selectedDomain = useMemo(
112
137
  () => (senderDetailsOptions || []).find(
113
- (d) => d.domainId === get(localSettings, DELIVERY_SETTING_KEY_DOMAIN_ID),
138
+ (senderDomain) => senderDomain.domainId === get(localSettings, DELIVERY_SETTING_KEY_DOMAIN_ID),
114
139
  ) || {},
115
- [senderDetailsOptions, localSettings.domainId],
140
+ [senderDetailsOptions, get(localSettings, DELIVERY_SETTING_KEY_DOMAIN_ID)],
116
141
  );
117
142
  const {
118
143
  gsmSenders: selectedDomainGsmSenders = [],
@@ -120,25 +145,103 @@ const ModifyDeliverySettings = (props) => {
120
145
  emailRepliers: selectedDomainEmailRepliers = [],
121
146
  } = selectedDomain;
122
147
 
148
+ const rcsSenderOptions = useMemo(
149
+ () => {
150
+ if (channel !== CHANNELS.RCS) return [];
151
+ const rcsDomainRows = senderDetailsOptions || [];
152
+ const selectedRcsDomainId = get(localSettings, DELIVERY_SETTING_KEY_DOMAIN_ID);
153
+ const rcsDomainsForDropdown =
154
+ selectedRcsDomainId != null && selectedRcsDomainId !== ''
155
+ ? rcsDomainRows.filter((senderDomain) => senderDomain?.domainId === selectedRcsDomainId)
156
+ : rcsDomainRows;
157
+ return rcsDomainsForDropdown.flatMap((senderDomain) =>
158
+ filterUsableGsmSendersForDomain(senderDomain, senderDomain?.gsmSenders, {
159
+ skipDomainNameEchoFilter: true,
160
+ }).map((gsmSenderRow) => ({
161
+ value: senderDomain?.domainId != null && gsmSenderRow?.value != null
162
+ ? `${senderDomain.domainId}|${gsmSenderRow.value}`
163
+ : gsmSenderRow?.value,
164
+ label: String(gsmSenderRow?.value ?? ''),
165
+ })));
166
+ },
167
+ [channel, senderDetailsOptions, localSettings],
168
+ );
169
+
170
+ const rcsGsmSenderIdForSelect = useMemo(() => {
171
+ const raw = get(localSettings, DELIVERY_SETTING_KEY_GSM_SENDER_ID);
172
+ if (raw == null || raw === '') return undefined;
173
+ const allowed = new Set((rcsSenderOptions || []).map((senderOption) => senderOption.value));
174
+ return allowed.has(raw) ? raw : undefined;
175
+ }, [localSettings, rcsSenderOptions]);
176
+
177
+ const smsFallbackDomainOptions = useMemo(
178
+ () => {
179
+ const raw = (smsFallbackSenderDetailsOptions || []).map((senderDetails) => ({
180
+ label: senderDetails.domainName || senderDetails.domainId,
181
+ value: senderDetails.domainId,
182
+ ...senderDetails,
183
+ }));
184
+ if (!(channel === CHANNELS.RCS && smsTraiDltEnabled && registeredSenderIds?.length)) {
185
+ return raw;
186
+ }
187
+ return raw.filter((domain) => (domain?.gsmSenders || []).some(
188
+ (gsmSenderOption) => registeredSenderIds?.includes(gsmSenderOption?.value),
189
+ ));
190
+ },
191
+ [channel, smsFallbackSenderDetailsOptions, smsTraiDltEnabled, registeredSenderIds],
192
+ );
193
+
194
+ const smsSenderOptions = useMemo(
195
+ () => {
196
+ if (channel !== CHANNELS.RCS) return [];
197
+ const smsFallbackDomainRows = smsFallbackSenderDetailsOptions || [];
198
+ const selectedSmsFallbackDomainId = get(localSettings, DELIVERY_SETTING_KEY_SMS_FALLBACK_DOMAIN_ID);
199
+ const smsFallbackDomainsForDropdown =
200
+ selectedSmsFallbackDomainId != null && selectedSmsFallbackDomainId !== ''
201
+ ? smsFallbackDomainRows.filter((senderDomain) => senderDomain?.domainId === selectedSmsFallbackDomainId)
202
+ : smsFallbackDomainRows;
203
+ const shouldFilterSmsSendersByDltRegistration = smsTraiDltEnabled && registeredSenderIds?.length;
204
+ return smsFallbackDomainsForDropdown.flatMap((senderDomain) =>
205
+ filterUsableGsmSendersForDomain(senderDomain, senderDomain?.gsmSenders, { skipDomainNameEchoFilter: true })
206
+ .filter((smsGsmSenderRow) =>
207
+ !shouldFilterSmsSendersByDltRegistration || registeredSenderIds?.includes(smsGsmSenderRow?.value))
208
+ .map((gsmSenderRow) => ({
209
+ value: senderDomain?.domainId != null && gsmSenderRow?.value != null
210
+ ? `${senderDomain.domainId}|${gsmSenderRow.value}`
211
+ : gsmSenderRow?.value,
212
+ label: String(gsmSenderRow?.value ?? ''),
213
+ })));
214
+ },
215
+ [channel, smsFallbackSenderDetailsOptions, localSettings, smsTraiDltEnabled, registeredSenderIds],
216
+ );
217
+
218
+ const smsFallbackGsmIdForSelect = useMemo(() => {
219
+ const raw = get(localSettings, DELIVERY_SETTING_KEY_CDMA_SENDER_ID);
220
+ if (raw == null || raw === '') return undefined;
221
+ const allowed = new Set((smsSenderOptions || []).map((senderOption) => senderOption.value));
222
+ return allowed.has(raw) ? raw : undefined;
223
+ }, [localSettings, smsSenderOptions]);
224
+
123
225
  const gsmOptions = useMemo(
124
226
  () => {
125
- const smsGsmSenders = channel === CHANNELS.SMS && smsTraiDltEnabled
126
- ? selectedDomainGsmSenders.filter((gsmId) => registeredSenderIds.includes(gsmId.value))
227
+ const smsGsmSendersForSelect = channel === CHANNELS.SMS && smsTraiDltEnabled && registeredSenderIds?.length
228
+ ? selectedDomainGsmSenders.filter((gsmSenderOption) =>
229
+ registeredSenderIds?.includes(gsmSenderOption?.value))
127
230
  : selectedDomainGsmSenders;
128
231
 
129
- return smsGsmSenders.map((o) => ({
130
- label: o.value || o.label,
131
- value: o.value,
232
+ return smsGsmSendersForSelect.map((gsmSenderRow) => ({
233
+ label: gsmSenderRow?.value || gsmSenderRow?.label,
234
+ value: gsmSenderRow?.value,
132
235
  }));
133
236
  },
134
237
  [channel, registeredSenderIds, selectedDomainGsmSenders, smsTraiDltEnabled],
135
238
  );
136
239
 
137
240
  const emailSenderOptions = useMemo(
138
- () => selectedDomainEmailSenders.map((o) => ({
139
- label: o.value || o.label,
140
- value: o.value,
141
- senderLabel: o.label,
241
+ () => selectedDomainEmailSenders.map((emailSenderRow) => ({
242
+ label: emailSenderRow.value || emailSenderRow.label,
243
+ value: emailSenderRow.value,
244
+ senderLabel: emailSenderRow.label,
142
245
  })),
143
246
  [selectedDomainEmailSenders],
144
247
  );
@@ -204,13 +307,54 @@ const ModifyDeliverySettings = (props) => {
204
307
  });
205
308
  }, []);
206
309
 
310
+ const onSmsFallbackDomainChange = useCallback(
311
+ (value) => {
312
+ setLocalSettings((prev) => {
313
+ const next = cloneDeep(prev);
314
+ next[DELIVERY_SETTING_KEY_SMS_FALLBACK_DOMAIN_ID] = value;
315
+ const selectedSmsFallbackDomain = (smsFallbackSenderDetailsOptions || []).find(
316
+ (senderDomain) => senderDomain?.domainId === value,
317
+ );
318
+ const shouldFilterSmsFallbackSendersByDltRegistration =
319
+ channel === CHANNELS.RCS && smsTraiDltEnabled && registeredSenderIds?.length;
320
+ const usableSmsFallbackGsmSenders = filterUsableGsmSendersForDomain(
321
+ selectedSmsFallbackDomain,
322
+ selectedSmsFallbackDomain?.gsmSenders,
323
+ { skipDomainNameEchoFilter: !!shouldFilterSmsFallbackSendersByDltRegistration },
324
+ );
325
+ const smsFallbackGsmSenders = shouldFilterSmsFallbackSendersByDltRegistration
326
+ ? usableSmsFallbackGsmSenders.filter((smsGsmSenderRow) =>
327
+ registeredSenderIds?.includes(smsGsmSenderRow?.value))
328
+ : usableSmsFallbackGsmSenders;
329
+ const firstSmsFallbackGsmSender = smsFallbackGsmSenders[0];
330
+ const composite = selectedSmsFallbackDomain?.domainId != null && firstSmsFallbackGsmSender?.value != null
331
+ ? `${selectedSmsFallbackDomain.domainId}|${firstSmsFallbackGsmSender.value}`
332
+ : (firstSmsFallbackGsmSender?.value || '');
333
+ next[DELIVERY_SETTING_KEY_CDMA_SENDER_ID] = composite;
334
+ return next;
335
+ });
336
+ },
337
+ [channel, smsFallbackSenderDetailsOptions, smsTraiDltEnabled, registeredSenderIds],
338
+ );
339
+
207
340
  const onDomainChange = useCallback(
208
341
  (value) => {
209
- const domain = domainOptions.find((d) => d.value === value);
342
+ const domain = domainOptions.find((domainOption) => domainOption.value === value);
210
343
  updateSetting(DELIVERY_SETTING_KEY_DOMAIN_ID, value);
211
344
  if (domain?.dgmId != null) updateSetting(DELIVERY_SETTING_KEY_DOMAIN_GATEWAY_MAP_ID, domain.dgmId);
345
+ if (channel === CHANNELS.RCS && domain) {
346
+ const usableRcsGsmSenders = filterUsableGsmSendersForDomain(domain, domain?.gsmSenders, {
347
+ skipDomainNameEchoFilter: true,
348
+ });
349
+ const firstRcsGsmSender = usableRcsGsmSenders[0];
350
+ const composite = domain?.domainId != null && firstRcsGsmSender?.value != null
351
+ ? `${domain.domainId}|${firstRcsGsmSender.value}`
352
+ : (firstRcsGsmSender?.value || '');
353
+ updateSetting(DELIVERY_SETTING_KEY_GSM_SENDER_ID, composite);
354
+ return;
355
+ }
212
356
  if (channel === CHANNELS.SMS && domain) {
213
- const smsGsmSenders = smsTraiDltEnabled
357
+ const smsGsmSenders = smsTraiDltEnabled && registeredSenderIds?.length
214
358
  ? (domain.gsmSenders || []).filter((gsmId) => registeredSenderIds.includes(gsmId.value))
215
359
  : domain.gsmSenders;
216
360
  const def = findDefault(smsGsmSenders);
@@ -230,9 +374,20 @@ const ModifyDeliverySettings = (props) => {
230
374
  );
231
375
 
232
376
  const handleDone = useCallback(() => {
233
- onSaveDeliverySettings(localSettings);
377
+ const next = cloneDeep(localSettings);
378
+ if (channel === CHANNELS.RCS) {
379
+ const rawRcs = get(next, DELIVERY_SETTING_KEY_GSM_SENDER_ID);
380
+ if (rawRcs && !(rcsSenderOptions || []).some((senderOption) => senderOption.value === rawRcs)) {
381
+ next[DELIVERY_SETTING_KEY_GSM_SENDER_ID] = '';
382
+ }
383
+ const rawSms = get(next, DELIVERY_SETTING_KEY_CDMA_SENDER_ID);
384
+ if (rawSms && !(smsSenderOptions || []).some((senderOption) => senderOption.value === rawSms)) {
385
+ next[DELIVERY_SETTING_KEY_CDMA_SENDER_ID] = '';
386
+ }
387
+ }
388
+ onSaveDeliverySettings(next);
234
389
  if (onClose) onClose();
235
- }, [localSettings, onSaveDeliverySettings, onClose]);
390
+ }, [channel, localSettings, onSaveDeliverySettings, onClose, rcsSenderOptions, smsSenderOptions]);
236
391
 
237
392
  const renderSelectRow = useCallback(
238
393
  ({
@@ -286,7 +441,7 @@ const ModifyDeliverySettings = (props) => {
286
441
  value: get(localSettings, DELIVERY_SETTING_KEY_SENDER_EMAIL),
287
442
  onChange: (val) => {
288
443
  const sender = selectedDomainEmailSenders.find(
289
- (s) => s.value === val,
444
+ (emailSenderRow) => emailSenderRow.value === val,
290
445
  );
291
446
  updateSetting(DELIVERY_SETTING_KEY_SENDER_EMAIL, val);
292
447
  updateSetting(DELIVERY_SETTING_KEY_SENDER_LABEL, sender?.label || val);
@@ -298,7 +453,7 @@ const ModifyDeliverySettings = (props) => {
298
453
  value: get(localSettings, DELIVERY_SETTING_KEY_SENDER_LABEL) || '',
299
454
  onChange: (val) => {
300
455
  const sender = selectedDomainEmailSenders.find(
301
- (s) => (s.label || s.value) === val,
456
+ (emailSenderRow) => (emailSenderRow.label || emailSenderRow.value) === val,
302
457
  );
303
458
  if (sender) {
304
459
  updateSetting(DELIVERY_SETTING_KEY_SENDER_EMAIL, sender.value);
@@ -326,7 +481,7 @@ const ModifyDeliverySettings = (props) => {
326
481
  onChange: (val) => {
327
482
  updateSetting(DELIVERY_SETTING_KEY_SOURCE_ACCOUNT_IDENTIFIER, val);
328
483
  const domain = (senderDetailsOptions || []).find(
329
- (d) => d.sourceAccountIdentifier === val,
484
+ (whatsappAccountRow) => whatsappAccountRow.sourceAccountIdentifier === val,
330
485
  );
331
486
  const first = domain?.gsmSenders?.[0];
332
487
  updateSetting(DELIVERY_SETTING_KEY_SENDER_MOB_NUM, first?.value || '');
@@ -340,10 +495,39 @@ const ModifyDeliverySettings = (props) => {
340
495
  onChange: (val) => updateSetting(DELIVERY_SETTING_KEY_SENDER_MOB_NUM, val),
341
496
  },
342
497
  ];
498
+ const rcsFields = [
499
+ {
500
+ titleMessage: messages.rcsDomainLabel,
501
+ options: domainOptions,
502
+ value: get(localSettings, DELIVERY_SETTING_KEY_DOMAIN_ID),
503
+ onChange: (val) => onDomainChange(val),
504
+ },
505
+ {
506
+ titleMessage: messages.rcsSenderIdLabel,
507
+ options: rcsSenderOptions,
508
+ value: rcsGsmSenderIdForSelect,
509
+ onChange: (val) => updateSetting(DELIVERY_SETTING_KEY_GSM_SENDER_ID, val),
510
+ },
511
+ ...((smsFallbackSenderDetailsOptions || []).length > 0 ? [
512
+ {
513
+ titleMessage: messages.fallbackSmsDomainLabel,
514
+ options: smsFallbackDomainOptions,
515
+ value: get(localSettings, DELIVERY_SETTING_KEY_SMS_FALLBACK_DOMAIN_ID),
516
+ onChange: onSmsFallbackDomainChange,
517
+ },
518
+ {
519
+ titleMessage: messages.fallbackSmsSenderIdLabel,
520
+ options: smsSenderOptions,
521
+ value: smsFallbackGsmIdForSelect,
522
+ onChange: (val) => updateSetting(DELIVERY_SETTING_KEY_CDMA_SENDER_ID, val),
523
+ },
524
+ ] : []),
525
+ ];
343
526
  const byChannel = {
344
527
  [CHANNELS.SMS]: smsFields,
345
528
  [CHANNELS.EMAIL]: emailFields,
346
529
  [CHANNELS.WHATSAPP]: whatsappFields,
530
+ [CHANNELS.RCS]: rcsFields,
347
531
  };
348
532
  return byChannel[channel] || [];
349
533
  }, [
@@ -356,6 +540,13 @@ const ModifyDeliverySettings = (props) => {
356
540
  wecrmAccountOptions,
357
541
  whatsappAccountOptionsForDisplay,
358
542
  whatsappSenderOptions,
543
+ rcsSenderOptions,
544
+ rcsGsmSenderIdForSelect,
545
+ smsSenderOptions,
546
+ smsFallbackGsmIdForSelect,
547
+ smsFallbackDomainOptions,
548
+ smsFallbackSenderDetailsOptions,
549
+ onSmsFallbackDomainChange,
359
550
  localSettings,
360
551
  selectedDomainEmailSenders,
361
552
  senderDetailsOptions,
@@ -390,9 +581,10 @@ const ModifyDeliverySettings = (props) => {
390
581
  };
391
582
 
392
583
  ModifyDeliverySettings.propTypes = {
393
- channel: PropTypes.oneOf([CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP]).isRequired,
584
+ channel: PropTypes.oneOf([CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP, CHANNELS.RCS]).isRequired,
394
585
  deliverySettings: PropTypes.object,
395
586
  senderDetailsOptions: PropTypes.array,
587
+ smsFallbackSenderDetailsOptions: PropTypes.array,
396
588
  wecrmAccounts: PropTypes.array,
397
589
  onSaveDeliverySettings: PropTypes.func.isRequired,
398
590
  onClose: PropTypes.func,
@@ -408,6 +600,7 @@ ModifyDeliverySettings.propTypes = {
408
600
  ModifyDeliverySettings.defaultProps = {
409
601
  deliverySettings: {},
410
602
  senderDetailsOptions: [],
603
+ smsFallbackSenderDetailsOptions: [],
411
604
  wecrmAccounts: [],
412
605
  onClose: undefined,
413
606
  isLoading: false,
@@ -21,6 +21,8 @@ export const DELIVERY_SETTING_KEY_SENDER_LABEL = 'senderLabel';
21
21
  export const DELIVERY_SETTING_KEY_SENDER_REPLY_TO = 'senderReplyTo';
22
22
  export const DELIVERY_SETTING_KEY_SOURCE_ACCOUNT_IDENTIFIER = 'sourceAccountIdentifier';
23
23
  export const DELIVERY_SETTING_KEY_SENDER_MOB_NUM = 'senderMobNum';
24
+ /** RCS: SMS fallback domain (separate from primary RCS domainId) */
25
+ export const DELIVERY_SETTING_KEY_SMS_FALLBACK_DOMAIN_ID = 'smsFallbackDomainId';
24
26
 
25
27
  /** data-testid for delivery settings edit icon */
26
28
  export const TEST_ID_DELIVERY_SETTINGS_EDIT = 'delivery-settings-edit';
@@ -32,6 +34,7 @@ export const CHANNELS_WITH_DELIVERY_SETTINGS = [
32
34
  CHANNELS.SMS,
33
35
  CHANNELS.EMAIL,
34
36
  CHANNELS.WHATSAPP,
37
+ CHANNELS.RCS,
35
38
  ];
36
39
 
37
40
  /** Default empty delivery setting per channel (campaigns-style shape) */
@@ -56,8 +59,21 @@ export const DEFAULT_DELIVERY_SETTING_WHATSAPP = {
56
59
  sourceAccountIdentifier: '',
57
60
  };
58
61
 
62
+ /** RCS: gsmSenderId = RCS sender, cdmaSenderId = SMS fallback sender (reuse same keys) */
63
+ export const DEFAULT_DELIVERY_SETTING_RCS = {
64
+ domainId: null,
65
+ domainGatewayMapId: null,
66
+ gsmSenderId: '',
67
+ smsFallbackDomainId: null,
68
+ cdmaSenderId: '',
69
+ };
70
+
59
71
  export const DEFAULT_DELIVERY_SETTINGS_BY_CHANNEL = {
60
72
  [CHANNELS.SMS]: DEFAULT_DELIVERY_SETTING_SMS,
61
73
  [CHANNELS.EMAIL]: DEFAULT_DELIVERY_SETTING_EMAIL,
62
74
  [CHANNELS.WHATSAPP]: DEFAULT_DELIVERY_SETTING_WHATSAPP,
75
+ [CHANNELS.RCS]: DEFAULT_DELIVERY_SETTING_RCS,
63
76
  };
77
+
78
+ /** API `contactInfo.type` keys that map to RCS-style GSM sender rows (env-specific naming). */
79
+ export const RCS_CONTACT_INFO_GSM_TYPE_KEYS = ['gsm_sender_id', 'rcs_sender_id', 'rcs_sender'];