@capillarytech/creatives-library 8.0.353-alpha.5 → 8.0.353-alpha.6
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.
- package/constants/unified.js +29 -0
- package/package.json +1 -1
- package/services/tests/api.test.js +35 -20
- package/utils/commonUtils.js +19 -1
- package/utils/rcsPayloadUtils.js +92 -0
- package/utils/templateVarUtils.js +201 -0
- package/utils/tests/rcsPayloadUtils.test.js +226 -0
- package/utils/tests/templateVarUtils.test.js +204 -0
- package/v2Components/CapActionButton/constants.js +7 -0
- package/v2Components/CapActionButton/index.js +166 -108
- package/v2Components/CapActionButton/index.scss +157 -6
- package/v2Components/CapActionButton/messages.js +19 -3
- package/v2Components/CapActionButton/tests/index.test.js +41 -17
- package/v2Components/CapTagList/index.js +10 -0
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +72 -49
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +213 -21
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +85 -10
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +79 -11
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
- package/v2Components/CommonTestAndPreview/UnifiedPreview/PreviewHeader.js +0 -17
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +157 -15
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +346 -146
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +138 -48
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
- package/v2Components/CommonTestAndPreview/constants.js +38 -4
- package/v2Components/CommonTestAndPreview/index.js +691 -235
- package/v2Components/CommonTestAndPreview/messages.js +45 -3
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
- package/v2Components/CommonTestAndPreview/sagas.js +25 -6
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +308 -284
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +231 -65
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
- package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +34 -13
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/PreviewHeader.test.js +0 -159
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -256
- package/v2Components/CommonTestAndPreview/tests/constants.test.js +1 -2
- package/v2Components/CommonTestAndPreview/tests/index.test.js +132 -198
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +36 -26
- package/v2Components/FormBuilder/index.js +11 -6
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +91 -0
- package/v2Components/SmsFallback/constants.js +73 -0
- package/v2Components/SmsFallback/index.js +956 -0
- package/v2Components/SmsFallback/index.scss +265 -0
- package/v2Components/SmsFallback/messages.js +78 -0
- package/v2Components/SmsFallback/smsFallbackUtils.js +119 -0
- package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
- package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
- package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
- package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +223 -0
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +309 -0
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
- package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
- package/v2Components/TemplatePreview/_templatePreview.scss +38 -23
- package/v2Components/TemplatePreview/constants.js +2 -0
- package/v2Components/TemplatePreview/index.js +143 -31
- package/v2Components/TemplatePreview/tests/index.test.js +142 -0
- package/v2Components/TestAndPreviewSlidebox/index.js +15 -3
- package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
- package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
- package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
- package/v2Components/VarSegmentMessageEditor/index.js +125 -0
- package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
- package/v2Containers/App/constants.js +0 -3
- package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +43 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +36 -4
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +10 -1
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
- package/v2Containers/CreativesContainer/constants.js +9 -0
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +79 -0
- package/v2Containers/CreativesContainer/index.js +322 -103
- package/v2Containers/CreativesContainer/index.scss +51 -1
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +78 -34
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +79 -16
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +357 -98
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
- package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
- package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
- package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
- package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
- package/v2Containers/Rcs/constants.js +119 -10
- package/v2Containers/Rcs/index.js +2445 -813
- package/v2Containers/Rcs/index.scss +280 -8
- package/v2Containers/Rcs/messages.js +34 -3
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +225 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +98018 -70073
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
- package/v2Containers/Rcs/tests/index.test.js +152 -121
- package/v2Containers/Rcs/tests/mockData.js +38 -0
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +318 -0
- package/v2Containers/Rcs/tests/utils.test.js +646 -30
- package/v2Containers/Rcs/utils.js +478 -11
- package/v2Containers/Sms/Create/index.js +106 -40
- package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
- package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
- package/v2Containers/SmsTrai/Create/index.js +9 -4
- package/v2Containers/SmsTrai/Edit/constants.js +2 -0
- package/v2Containers/SmsTrai/Edit/index.js +640 -130
- package/v2Containers/SmsTrai/Edit/index.scss +121 -0
- package/v2Containers/SmsTrai/Edit/messages.js +14 -4
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4328 -2375
- package/v2Containers/SmsWrapper/index.js +37 -8
- package/v2Containers/TagList/index.js +6 -0
- package/v2Containers/Templates/TemplatesActionBar.js +101 -0
- package/v2Containers/Templates/_templates.scss +166 -9
- package/v2Containers/Templates/actions.js +11 -0
- package/v2Containers/Templates/constants.js +2 -0
- package/v2Containers/Templates/index.js +122 -120
- package/v2Containers/Templates/sagas.js +56 -12
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1062 -1017
- package/v2Containers/Templates/tests/sagas.test.js +199 -16
- package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
- package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
- package/v2Containers/TemplatesV2/index.js +86 -23
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
- package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
- package/v2Containers/WebPush/Create/index.js +8 -91
- package/v2Containers/WebPush/Create/index.scss +0 -7
- package/v2Containers/Whatsapp/index.js +3 -20
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
- package/v2Components/CommonTestAndPreview/UnifiedPreview/WebPushPreviewContent.js +0 -169
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/WebPushPreviewContent.test.js +0 -522
- package/v2Containers/App/tests/constants.test.js +0 -61
- package/v2Containers/Templates/tests/webpush.test.js +0 -375
- package/v2Containers/WebPush/Create/tests/getTemplateContent.test.js +0 -338
- package/v2Containers/WebPush/Create/tests/testAndPreviewIntegration.test.js +0 -325
|
@@ -31,7 +31,8 @@ import CustomValuesEditor from './CustomValuesEditor';
|
|
|
31
31
|
import SendTestMessage from './SendTestMessage';
|
|
32
32
|
import PreviewSection from './PreviewSection';
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
import * as Api from '../../services/api';
|
|
35
|
+
import { extractTemplateVariables } from '../../utils/templateVarUtils';
|
|
35
36
|
import AddTestCustomerButton from './AddTestCustomer';
|
|
36
37
|
import ExistingCustomerModal from './ExistingCustomerModal';
|
|
37
38
|
// Import constants
|
|
@@ -81,12 +82,23 @@ import {
|
|
|
81
82
|
IMAGE,
|
|
82
83
|
VIDEO,
|
|
83
84
|
URL,
|
|
85
|
+
PREVIEW_TAB_RCS,
|
|
86
|
+
PREVIEW_TAB_SMS_FALLBACK,
|
|
84
87
|
CHANNELS_USING_ANDROID_PREVIEW_DEVICE,
|
|
85
|
-
|
|
88
|
+
RCS_TEST_META_CONTENT_TYPE_RICHCARD,
|
|
89
|
+
RCS_TEST_META_CARD_TYPE_STANDALONE,
|
|
90
|
+
RCS_TEST_META_CARD_ORIENTATION_VERTICAL,
|
|
91
|
+
RCS_TEST_META_CARD_WIDTH_SMALL,
|
|
92
|
+
SMS_MUSTACHE_TAG_PATTERN,
|
|
86
93
|
} from './constants';
|
|
87
|
-
|
|
88
|
-
// Import utilities
|
|
89
94
|
import { getCdnUrl } from '../../utils/cdnTransformation';
|
|
95
|
+
import {
|
|
96
|
+
normalizePreviewApiPayload,
|
|
97
|
+
extractPreviewFromLiquidResponse,
|
|
98
|
+
getSmsFallbackTextForTagExtraction,
|
|
99
|
+
} from './previewApiUtils';
|
|
100
|
+
import { pickFirstSmsFallbackTemplateString } from '../../v2Containers/Rcs/rcsLibraryHydrationUtils';
|
|
101
|
+
|
|
90
102
|
import { isValidEmail, isValidMobile, formatPhoneNumber } from '../../utils/commonUtils';
|
|
91
103
|
import { getMembersLookup } from '../../services/api';
|
|
92
104
|
|
|
@@ -109,6 +121,85 @@ const filterUsableGsmSendersForDomain = (domain, gsmSenders, { skipDomainNameEch
|
|
|
109
121
|
});
|
|
110
122
|
};
|
|
111
123
|
|
|
124
|
+
/** Preview payload from Redux may be an Immutable Map — normalize for React state. */
|
|
125
|
+
const toPlainPreviewData = (data) => {
|
|
126
|
+
if (data == null) return null;
|
|
127
|
+
const plain = typeof data.toJS === 'function' ? data.toJS() : data;
|
|
128
|
+
return normalizePreviewApiPayload(plain);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Merge existing customValues with tag keys from categorized groups.
|
|
133
|
+
* Each group is { required, optional } (arrays of tag objects with fullPath).
|
|
134
|
+
* Preserves existing values; adds '' for any tag key not yet present.
|
|
135
|
+
* Reusable for RCS+fallback and any flow that needs to ensure customValues has keys for tags.
|
|
136
|
+
*/
|
|
137
|
+
const mergeCustomValuesWithTagKeys = (prev, ...categorizedGroups) => {
|
|
138
|
+
const next = { ...(prev || {}) };
|
|
139
|
+
categorizedGroups.forEach((group) => {
|
|
140
|
+
[...(group.required || []), ...(group.optional || [])].forEach((tag) => {
|
|
141
|
+
const key = tag?.fullPath;
|
|
142
|
+
if (key && next[key] === undefined) next[key] = '';
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
return next;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/** True when `body` contains `{{name}}` mustache tokens (user-fillable personalization tags).
|
|
149
|
+
* DLT `{#name#}` slots are pre-bound template variables and are intentionally excluded. */
|
|
150
|
+
const smsTemplateHasMustacheTags = (body) =>
|
|
151
|
+
typeof body === 'string' && SMS_MUSTACHE_TAG_PATTERN.test(body);
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Build tag rows from `{{…}}` mustache tokens only — DLT `{#…#}` slots are excluded because
|
|
155
|
+
* they are pre-bound template variables, not user-fillable personalization tags.
|
|
156
|
+
* Passing a mustache-only captureRegex to extractTemplateVariables skips the DLT branch.
|
|
157
|
+
* A non-global regex is used so ensureGlobalRegexForExecLoop creates a fresh instance on each call.
|
|
158
|
+
*/
|
|
159
|
+
const buildSyntheticSmsMustacheTags = (body = '') => {
|
|
160
|
+
if (!body || typeof body !== 'string') return [];
|
|
161
|
+
return extractTemplateVariables(body, /\{\{([^}]+)\}\}/).map((name) => ({
|
|
162
|
+
name,
|
|
163
|
+
metaData: { userDriven: false },
|
|
164
|
+
children: [],
|
|
165
|
+
}));
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/** RCS createMessageMeta: media shape (mediaUrl, thumbnailUrl, height string). */
|
|
169
|
+
const normalizeRcsTestCardMedia = (media) => {
|
|
170
|
+
if (!media || typeof media !== 'object') return undefined;
|
|
171
|
+
const mediaUrl =
|
|
172
|
+
media.mediaUrl != null && String(media.mediaUrl).trim() !== ''
|
|
173
|
+
? String(media.mediaUrl)
|
|
174
|
+
: media.url != null && String(media.url).trim() !== ''
|
|
175
|
+
? String(media.url)
|
|
176
|
+
: '';
|
|
177
|
+
const thumbnailUrl = media.thumbnailUrl != null ? String(media.thumbnailUrl) : '';
|
|
178
|
+
const height = media.height != null ? String(media.height) : undefined;
|
|
179
|
+
const out = { mediaUrl, thumbnailUrl };
|
|
180
|
+
if (height) out.height = height;
|
|
181
|
+
return out;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
/** RCS createMessageMeta: suggestion shape (index, type, text, phoneNumber, url, postback). */
|
|
185
|
+
const mapRcsSuggestionForTestMeta = (suggestionRow, index) => ({
|
|
186
|
+
index,
|
|
187
|
+
type: suggestionRow?.type ?? '',
|
|
188
|
+
text: suggestionRow?.text != null ? String(suggestionRow.text) : '',
|
|
189
|
+
phoneNumber:
|
|
190
|
+
suggestionRow?.phoneNumber != null
|
|
191
|
+
? String(suggestionRow.phoneNumber)
|
|
192
|
+
: suggestionRow?.phone_number != null
|
|
193
|
+
? String(suggestionRow.phone_number)
|
|
194
|
+
: '',
|
|
195
|
+
url: suggestionRow?.url !== undefined ? suggestionRow.url : null,
|
|
196
|
+
postback:
|
|
197
|
+
suggestionRow?.postback != null
|
|
198
|
+
? String(suggestionRow.postback)
|
|
199
|
+
: suggestionRow?.text != null
|
|
200
|
+
? String(suggestionRow.text)
|
|
201
|
+
: '',
|
|
202
|
+
});
|
|
112
203
|
|
|
113
204
|
/**
|
|
114
205
|
* CapTreeSelect and group resolution use strict equality; API data may mix numeric and string ids.
|
|
@@ -134,7 +225,7 @@ const testEntityIdsEqual = (a, b) => {
|
|
|
134
225
|
*/
|
|
135
226
|
const CommonTestAndPreview = (props) => {
|
|
136
227
|
const {
|
|
137
|
-
intl: { formatMessage },
|
|
228
|
+
intl: { formatMessage, locale: userLocale = 'en' },
|
|
138
229
|
show,
|
|
139
230
|
onClose,
|
|
140
231
|
channel, // The channel: 'EMAIL', 'SMS', 'RCS', etc.
|
|
@@ -170,12 +261,21 @@ const CommonTestAndPreview = (props) => {
|
|
|
170
261
|
...additionalProps
|
|
171
262
|
} = props;
|
|
172
263
|
|
|
264
|
+
const smsFallbackContent = additionalProps?.smsFallbackContent;
|
|
265
|
+
const smsFallbackTextForTagExtraction = useMemo(
|
|
266
|
+
() => getSmsFallbackTextForTagExtraction(smsFallbackContent),
|
|
267
|
+
[smsFallbackContent],
|
|
268
|
+
);
|
|
173
269
|
// ============================================
|
|
174
270
|
// STATE MANAGEMENT
|
|
175
271
|
// ============================================
|
|
176
272
|
const [selectedCustomer, setSelectedCustomer] = useState(null);
|
|
177
273
|
const [requiredTags, setRequiredTags] = useState([]);
|
|
178
274
|
const [optionalTags, setOptionalTags] = useState([]);
|
|
275
|
+
const [smsFallbackExtractedTags, setSmsFallbackExtractedTags] = useState([]);
|
|
276
|
+
const [smsFallbackRequiredTags, setSmsFallbackRequiredTags] = useState([]);
|
|
277
|
+
const [smsFallbackOptionalTags, setSmsFallbackOptionalTags] = useState([]);
|
|
278
|
+
const [isExtractingSmsFallbackTags, setIsExtractingSmsFallbackTags] = useState(false);
|
|
179
279
|
const [customValues, setCustomValues] = useState({});
|
|
180
280
|
const [showJSON, setShowJSON] = useState(false);
|
|
181
281
|
const [tagsExtracted, setTagsExtracted] = useState(false);
|
|
@@ -187,6 +287,8 @@ const CommonTestAndPreview = (props) => {
|
|
|
187
287
|
const [customerData, setCustomerData] = useState({ name: '', email: '', mobile: '', customerId: '' });
|
|
188
288
|
|
|
189
289
|
const [previewDevice, setPreviewDevice] = useState(initialDevice);
|
|
290
|
+
const [activePreviewTab, setActivePreviewTab] = useState(PREVIEW_TAB_RCS);
|
|
291
|
+
const [smsFallbackPreviewText, setSmsFallbackPreviewText] = useState(undefined);
|
|
190
292
|
// Track if a preview call has been made (to know when to use previewDataHtml vs raw content)
|
|
191
293
|
const [hasPreviewCallBeenMade, setHasPreviewCallBeenMade] = useState(false);
|
|
192
294
|
const [previewDataHtml, setPreviewDataHtml] = useState(() => {
|
|
@@ -219,15 +321,22 @@ const CommonTestAndPreview = (props) => {
|
|
|
219
321
|
[CHANNELS.WHATSAPP]: {
|
|
220
322
|
domainId: null, senderMobNum: '', sourceAccountIdentifier: '',
|
|
221
323
|
},
|
|
324
|
+
[CHANNELS.RCS]: {
|
|
325
|
+
domainId: null,
|
|
326
|
+
domainGatewayMapId: null,
|
|
327
|
+
gsmSenderId: '',
|
|
328
|
+
smsFallbackDomainId: null,
|
|
329
|
+
cdmaSenderId: '', // gsmSenderId = RCS sender (domainId|senderId), cdmaSenderId = SMS fallback
|
|
330
|
+
},
|
|
222
331
|
});
|
|
223
332
|
|
|
224
|
-
const channelsWithDeliverySettings = [CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP];
|
|
333
|
+
const channelsWithDeliverySettings = [CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP, CHANNELS.RCS];
|
|
225
334
|
const formDataForSendTest = formData ?? (content && typeof content === 'object' && !Array.isArray(content) ? content : formData);
|
|
226
335
|
const smsTemplateConfigs = formDataForSendTest?.templateConfigs || {};
|
|
227
336
|
const smsTraiDltEnabled = !!smsTemplateConfigs?.traiDltEnabled;
|
|
228
337
|
const registeredSenderIds = smsTemplateConfigs?.registeredSenderIds || [];
|
|
229
338
|
|
|
230
|
-
// Fetch sender details and WeCRM accounts when Test & Preview opens
|
|
339
|
+
// Fetch sender details and WeCRM accounts when Test & Preview opens (SMS, Email, WhatsApp, RCS — same process)
|
|
231
340
|
useEffect(() => {
|
|
232
341
|
if (!show || !channel) {
|
|
233
342
|
return;
|
|
@@ -236,39 +345,99 @@ const CommonTestAndPreview = (props) => {
|
|
|
236
345
|
if (actions.getSenderDetailsRequested) {
|
|
237
346
|
actions.getSenderDetailsRequested({ channel, orgUnitId: orgUnitId ?? -1 });
|
|
238
347
|
}
|
|
348
|
+
// SMS domains/senders are needed for RCS delivery UI (fallback row + slidebox) whenever RCS is open — not only when fallback body exists.
|
|
349
|
+
if (channel === CHANNELS.RCS && actions.getSenderDetailsRequested) {
|
|
350
|
+
actions.getSenderDetailsRequested({ channel: CHANNELS.SMS, orgUnitId: orgUnitId ?? -1 });
|
|
351
|
+
}
|
|
239
352
|
if (channel === CHANNELS.WHATSAPP && actions.getWeCrmAccountsRequested) {
|
|
240
353
|
actions.getWeCrmAccountsRequested({ sourceName: CHANNELS.WHATSAPP });
|
|
241
354
|
}
|
|
242
355
|
}
|
|
243
356
|
}, [show, channel, orgUnitId, actions]);
|
|
244
357
|
|
|
245
|
-
useEffect(() => {
|
|
246
|
-
if (!show) {
|
|
247
|
-
setCustomerModal([false, '']);
|
|
248
|
-
setSearchValue('');
|
|
249
|
-
setCustomerData({ name: '', email: '', mobile: '', customerId: '' });
|
|
250
|
-
}
|
|
251
|
-
}, [show]);
|
|
252
|
-
|
|
253
358
|
const findDefault = (arr) => (arr && arr.find((x) => x.default)) || (arr && arr[0]) || {};
|
|
254
359
|
|
|
255
360
|
// Auto-set default delivery setting when sender details load (campaigns-style: first domain + default/first sender)
|
|
256
361
|
useEffect(() => {
|
|
257
362
|
if (!channel || !channelsWithDeliverySettings.includes(channel)) return;
|
|
363
|
+
|
|
364
|
+
if (channel === CHANNELS.RCS) {
|
|
365
|
+
const rcsDomainRows = senderDetailsByChannel?.[CHANNELS.RCS] || [];
|
|
366
|
+
const smsFallbackDomainRows = senderDetailsByChannel?.[CHANNELS.SMS] || [];
|
|
367
|
+
if (!rcsDomainRows.length) return;
|
|
368
|
+
|
|
369
|
+
const currentRcsDeliverySettings = testPreviewDeliverySettings?.[CHANNELS.RCS] || {};
|
|
370
|
+
const isRcsGsmSenderUnset = !currentRcsDeliverySettings?.gsmSenderId;
|
|
371
|
+
const isSmsFallbackSenderUnset = !currentRcsDeliverySettings?.cdmaSenderId;
|
|
372
|
+
const isRcsDeliveryFullyUnset = isRcsGsmSenderUnset && isSmsFallbackSenderUnset;
|
|
373
|
+
const shouldOnlyFillSmsFallbackSender =
|
|
374
|
+
!isRcsGsmSenderUnset && isSmsFallbackSenderUnset && smsFallbackDomainRows.length > 0;
|
|
375
|
+
|
|
376
|
+
if (!isRcsDeliveryFullyUnset && !shouldOnlyFillSmsFallbackSender) return;
|
|
377
|
+
|
|
378
|
+
const firstRcsDomain = rcsDomainRows[0];
|
|
379
|
+
const firstSmsFallbackDomain = smsFallbackDomainRows[0];
|
|
380
|
+
const usableRcsGsmSenders = filterUsableGsmSendersForDomain(
|
|
381
|
+
firstRcsDomain,
|
|
382
|
+
firstRcsDomain?.gsmSenders,
|
|
383
|
+
{ skipDomainNameEchoFilter: true },
|
|
384
|
+
);
|
|
385
|
+
const usableSmsFallbackGsmSenders = firstSmsFallbackDomain
|
|
386
|
+
? filterUsableGsmSendersForDomain(firstSmsFallbackDomain, firstSmsFallbackDomain?.gsmSenders)
|
|
387
|
+
: [];
|
|
388
|
+
const defaultRcsGsmSender = usableRcsGsmSenders[0];
|
|
389
|
+
const defaultSmsFallbackGsmSender = usableSmsFallbackGsmSenders[0];
|
|
390
|
+
const rcsSenderCompositeValue =
|
|
391
|
+
firstRcsDomain?.domainId != null && defaultRcsGsmSender?.value != null
|
|
392
|
+
? `${firstRcsDomain.domainId}|${defaultRcsGsmSender.value}`
|
|
393
|
+
: (defaultRcsGsmSender?.value || '');
|
|
394
|
+
const smsFallbackSenderCompositeValue =
|
|
395
|
+
firstSmsFallbackDomain?.domainId != null && defaultSmsFallbackGsmSender?.value != null
|
|
396
|
+
? `${firstSmsFallbackDomain.domainId}|${defaultSmsFallbackGsmSender.value}`
|
|
397
|
+
: (defaultSmsFallbackGsmSender?.value || '');
|
|
398
|
+
|
|
399
|
+
setTestPreviewDeliverySettings((prev) => {
|
|
400
|
+
const previousRcsSettings = prev?.[CHANNELS.RCS] || {};
|
|
401
|
+
if (shouldOnlyFillSmsFallbackSender) {
|
|
402
|
+
if (!smsFallbackSenderCompositeValue) return prev;
|
|
403
|
+
return {
|
|
404
|
+
...prev,
|
|
405
|
+
[CHANNELS.RCS]: {
|
|
406
|
+
...previousRcsSettings,
|
|
407
|
+
smsFallbackDomainId: firstSmsFallbackDomain?.domainId ?? null,
|
|
408
|
+
cdmaSenderId: smsFallbackSenderCompositeValue,
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
return {
|
|
413
|
+
...prev,
|
|
414
|
+
[CHANNELS.RCS]: {
|
|
415
|
+
domainId: firstRcsDomain?.domainId ?? null,
|
|
416
|
+
domainGatewayMapId: firstRcsDomain?.dgmId ?? null,
|
|
417
|
+
gsmSenderId: rcsSenderCompositeValue,
|
|
418
|
+
smsFallbackDomainId: firstSmsFallbackDomain?.domainId ?? null,
|
|
419
|
+
cdmaSenderId: smsFallbackSenderCompositeValue,
|
|
420
|
+
},
|
|
421
|
+
};
|
|
422
|
+
});
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
|
|
258
426
|
const domains = senderDetailsByChannel[channel];
|
|
259
427
|
if (!domains || domains.length === 0) return;
|
|
260
428
|
const {
|
|
261
|
-
domainId = '', gsmSenderId = '', senderEmail = '', senderMobNum = '',
|
|
429
|
+
domainId = '', gsmSenderId = '', cdmaSenderId = '', senderEmail = '', senderMobNum = '',
|
|
262
430
|
} = testPreviewDeliverySettings[channel] || {};
|
|
263
|
-
const isEmptySelection = !domainId && !gsmSenderId && !senderEmail && !senderMobNum;
|
|
431
|
+
const isEmptySelection = !domainId && !gsmSenderId && !cdmaSenderId && !senderEmail && !senderMobNum;
|
|
264
432
|
if (!isEmptySelection) return;
|
|
265
433
|
|
|
266
434
|
const whatsappAccountFromForm = channel === CHANNELS.WHATSAPP ? formData?.accountName : undefined;
|
|
267
435
|
const matchedWhatsappAccount = whatsappAccountFromForm
|
|
268
436
|
? (wecrmAccounts || []).find((account) => account?.name === whatsappAccountFromForm)
|
|
269
437
|
: null;
|
|
270
|
-
const smsDomains = channel === CHANNELS.SMS && smsTraiDltEnabled
|
|
271
|
-
? domains.filter((domain) => (domain
|
|
438
|
+
const smsDomains = channel === CHANNELS.SMS && smsTraiDltEnabled && registeredSenderIds?.length
|
|
439
|
+
? domains.filter((domain) => (domain?.gsmSenders || []).some((gsm) =>
|
|
440
|
+
registeredSenderIds?.includes(gsm?.value)))
|
|
272
441
|
: domains;
|
|
273
442
|
const [defaultDomain] = domains;
|
|
274
443
|
const [firstSmsDomain] = smsDomains;
|
|
@@ -283,8 +452,8 @@ const CommonTestAndPreview = (props) => {
|
|
|
283
452
|
const next = { ...prev };
|
|
284
453
|
if (channel === CHANNELS.SMS) {
|
|
285
454
|
const smsGsmSenders = smsTraiDltEnabled
|
|
286
|
-
? (firstDomain
|
|
287
|
-
: firstDomain
|
|
455
|
+
? (firstDomain?.gsmSenders || []).filter((gsm) => registeredSenderIds?.includes(gsm?.value))
|
|
456
|
+
: firstDomain?.gsmSenders;
|
|
288
457
|
next[channel] = {
|
|
289
458
|
domainId: firstDomain.domainId,
|
|
290
459
|
domainGatewayMapId: firstDomain.dgmId,
|
|
@@ -317,10 +486,29 @@ const CommonTestAndPreview = (props) => {
|
|
|
317
486
|
// MEMOIZED VALUES
|
|
318
487
|
// ============================================
|
|
319
488
|
|
|
489
|
+
const allTags = useMemo(
|
|
490
|
+
() => [...requiredTags, ...optionalTags, ...smsFallbackRequiredTags, ...smsFallbackOptionalTags],
|
|
491
|
+
[requiredTags, optionalTags, smsFallbackRequiredTags, smsFallbackOptionalTags]
|
|
492
|
+
);
|
|
493
|
+
|
|
494
|
+
const allRequiredTags = useMemo(
|
|
495
|
+
() => [...requiredTags, ...smsFallbackRequiredTags],
|
|
496
|
+
[requiredTags, smsFallbackRequiredTags]
|
|
497
|
+
);
|
|
498
|
+
|
|
499
|
+
const buildEmptyValues = useCallback(
|
|
500
|
+
() => allTags.reduce((acc, tag) => {
|
|
501
|
+
const key = tag?.fullPath;
|
|
502
|
+
if (key) acc[key] = '';
|
|
503
|
+
return acc;
|
|
504
|
+
}, {}),
|
|
505
|
+
[allTags]
|
|
506
|
+
);
|
|
507
|
+
|
|
320
508
|
// Check if update preview button should be disabled
|
|
321
509
|
const isUpdatePreviewDisabled = useMemo(() => (
|
|
322
|
-
|
|
323
|
-
), [
|
|
510
|
+
allRequiredTags.some((tag) => !customValues[tag.fullPath])
|
|
511
|
+
), [allRequiredTags, customValues]);
|
|
324
512
|
|
|
325
513
|
// Get current content based on channel and editor type
|
|
326
514
|
const getCurrentContent = useMemo(() => {
|
|
@@ -364,6 +552,13 @@ const CommonTestAndPreview = (props) => {
|
|
|
364
552
|
return currentTabData.base['sms-editor'];
|
|
365
553
|
}
|
|
366
554
|
}
|
|
555
|
+
// DLT / Test & Preview shape: { templateConfigs: { template, templateId, ... } }
|
|
556
|
+
if (formData.templateConfigs?.template) {
|
|
557
|
+
const smsDltTemplateValue = formData.templateConfigs.template;
|
|
558
|
+
if (typeof smsDltTemplateValue === 'string') return smsDltTemplateValue;
|
|
559
|
+
if (Array.isArray(smsDltTemplateValue)) return smsDltTemplateValue.join('');
|
|
560
|
+
return '';
|
|
561
|
+
}
|
|
367
562
|
}
|
|
368
563
|
|
|
369
564
|
// SMS channel fallback - if formData is not available, use content directly
|
|
@@ -409,7 +604,70 @@ const CommonTestAndPreview = (props) => {
|
|
|
409
604
|
return content || '';
|
|
410
605
|
}, [channel, formData, currentTab, beeContent, content, beeInstance]);
|
|
411
606
|
|
|
412
|
-
|
|
607
|
+
const leftPanelExtractedTags = useMemo(() => {
|
|
608
|
+
if (channel === CHANNELS.SMS) {
|
|
609
|
+
const smsEditorBody = typeof getCurrentContent === 'string' ? getCurrentContent : '';
|
|
610
|
+
if (!smsTemplateHasMustacheTags(smsEditorBody)) return [];
|
|
611
|
+
const extractTagsFromApi = extractedTags ?? [];
|
|
612
|
+
if (extractTagsFromApi.length > 0) return extractTagsFromApi;
|
|
613
|
+
return buildSyntheticSmsMustacheTags(smsEditorBody);
|
|
614
|
+
}
|
|
615
|
+
const hasFallbackSmsBody = !!(smsFallbackContent?.templateContent || smsFallbackContent?.content);
|
|
616
|
+
if (channel === CHANNELS.RCS && hasFallbackSmsBody) {
|
|
617
|
+
const rcsPrimaryTags = extractedTags ?? [];
|
|
618
|
+
const fallbackSmsTextForTags = smsFallbackTextForTagExtraction ?? '';
|
|
619
|
+
const fallbackSmsTagRows = smsTemplateHasMustacheTags(fallbackSmsTextForTags)
|
|
620
|
+
? (smsFallbackExtractedTags?.length > 0
|
|
621
|
+
? smsFallbackExtractedTags
|
|
622
|
+
: buildSyntheticSmsMustacheTags(fallbackSmsTextForTags))
|
|
623
|
+
: [];
|
|
624
|
+
const mergedRcsAndFallbackTags = [...rcsPrimaryTags, ...fallbackSmsTagRows];
|
|
625
|
+
if (mergedRcsAndFallbackTags.length > 0) return mergedRcsAndFallbackTags;
|
|
626
|
+
return buildSyntheticSmsMustacheTags(fallbackSmsTextForTags);
|
|
627
|
+
}
|
|
628
|
+
return extractedTags ?? [];
|
|
629
|
+
}, [
|
|
630
|
+
channel,
|
|
631
|
+
extractedTags,
|
|
632
|
+
getCurrentContent,
|
|
633
|
+
smsFallbackContent,
|
|
634
|
+
smsFallbackExtractedTags,
|
|
635
|
+
smsFallbackTextForTagExtraction,
|
|
636
|
+
]);
|
|
637
|
+
|
|
638
|
+
const isRcsSmsFallbackPreviewEnabled =
|
|
639
|
+
channel === CHANNELS.RCS
|
|
640
|
+
&& !!(smsFallbackContent?.templateContent || smsFallbackContent?.content);
|
|
641
|
+
// Only treat as SMS when user is on the Fallback SMS tab — not whenever fallback exists (RCS tab needs RCS preview API).
|
|
642
|
+
const isSmsFallbackTabActive = isRcsSmsFallbackPreviewEnabled && activePreviewTab === PREVIEW_TAB_SMS_FALLBACK;
|
|
643
|
+
const activeChannelForActions = isSmsFallbackTabActive ? CHANNELS.SMS : channel;
|
|
644
|
+
// VarSegment slot values live in rcsSmsFallbackVarMapped; raw templateContent alone is stale for /preview Body.
|
|
645
|
+
const resolvedSmsFallbackBodyForPreviewTab =
|
|
646
|
+
smsFallbackTextForTagExtraction
|
|
647
|
+
|| smsFallbackContent?.templateContent
|
|
648
|
+
|| smsFallbackContent?.content
|
|
649
|
+
|| '';
|
|
650
|
+
const activeContentForActions = isSmsFallbackTabActive
|
|
651
|
+
? resolvedSmsFallbackBodyForPreviewTab
|
|
652
|
+
: getCurrentContent;
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* SMS fallback pane must show /preview API result when user updated preview on that tab (plain text).
|
|
656
|
+
* Skip when resolvedBody is RCS-shaped (e.g. user last previewed on RCS tab).
|
|
657
|
+
*/
|
|
658
|
+
// smsFallbackPreviewText is the single source of truth for the resolved SMS fallback preview.
|
|
659
|
+
// It is set only by syncSmsFallbackPreview (called from handleUpdatePreview and the
|
|
660
|
+
// prefilled-values effect) and reset to undefined on discard / slidebox close.
|
|
661
|
+
// Using previewDataHtml as a fallback is unsafe because that state is shared with the primary
|
|
662
|
+
// RCS preview and can contain stale SMS or RCS content.
|
|
663
|
+
const smsFallbackResolvedText = useMemo(() => {
|
|
664
|
+
const hasFallbackBody = !!(smsFallbackContent?.templateContent || smsFallbackContent?.content);
|
|
665
|
+
if (channel !== CHANNELS.RCS || !hasFallbackBody) return undefined;
|
|
666
|
+
if (smsFallbackPreviewText != null) return smsFallbackPreviewText;
|
|
667
|
+
return undefined;
|
|
668
|
+
}, [channel, smsFallbackContent, smsFallbackPreviewText]);
|
|
669
|
+
|
|
670
|
+
// Build test entities tree data from testCustomers prop
|
|
413
671
|
// Build test entities tree data from testCustomers prop (includes customers added via addTestCustomer action)
|
|
414
672
|
const testEntitiesTreeData = useMemo(() => {
|
|
415
673
|
const groupsNode = {
|
|
@@ -418,7 +676,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
418
676
|
selectable: false,
|
|
419
677
|
children: testGroups?.map((group) => ({
|
|
420
678
|
title: group?.groupName,
|
|
421
|
-
value:
|
|
679
|
+
value: normalizeTestEntityId(group?.groupId),
|
|
422
680
|
})),
|
|
423
681
|
};
|
|
424
682
|
|
|
@@ -428,7 +686,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
428
686
|
selectable: false,
|
|
429
687
|
children: testCustomers?.map((customer) => ({
|
|
430
688
|
title: customer?.name?.trim() || customer?.email?.trim() || customer?.mobile?.trim() || customer?.userId || customer?.customerId,
|
|
431
|
-
value:
|
|
689
|
+
value: normalizeTestEntityId(customer?.userId ?? customer?.customerId),
|
|
432
690
|
})) || [],
|
|
433
691
|
};
|
|
434
692
|
|
|
@@ -508,11 +766,10 @@ const CommonTestAndPreview = (props) => {
|
|
|
508
766
|
email: customerData?.email || '',
|
|
509
767
|
mobile: customerData?.mobile || '',
|
|
510
768
|
});
|
|
511
|
-
const prefixedAddedId = 'customer:' + normalizedAddedId;
|
|
512
769
|
setSelectedTestEntities((prev) => (
|
|
513
|
-
prev.some((id) => id
|
|
770
|
+
prev.some((id) => testEntityIdsEqual(id, normalizedAddedId))
|
|
514
771
|
? prev
|
|
515
|
-
: [...prev,
|
|
772
|
+
: [...prev, normalizedAddedId]
|
|
516
773
|
));
|
|
517
774
|
}
|
|
518
775
|
handleCloseCustomerModal();
|
|
@@ -611,17 +868,42 @@ const CommonTestAndPreview = (props) => {
|
|
|
611
868
|
messageBody: contentStr,
|
|
612
869
|
};
|
|
613
870
|
|
|
614
|
-
case CHANNELS.WEBPUSH:
|
|
615
|
-
return {
|
|
616
|
-
...basePayload,
|
|
617
|
-
messageBody: contentStr,
|
|
618
|
-
};
|
|
619
|
-
|
|
620
871
|
default:
|
|
621
872
|
return basePayload;
|
|
622
873
|
}
|
|
623
874
|
};
|
|
624
875
|
|
|
876
|
+
/**
|
|
877
|
+
* When RCS has SMS fallback, refresh fallback preview text via the same Liquid /preview API
|
|
878
|
+
* (separate call with SMS channel + fallback template body). Used after primary preview updates.
|
|
879
|
+
*/
|
|
880
|
+
const syncSmsFallbackPreview = async (customValuesForResolve, selectedCustomerObj) => {
|
|
881
|
+
const fallbackBodyForLiquidPreview =
|
|
882
|
+
getSmsFallbackTextForTagExtraction(smsFallbackContent)
|
|
883
|
+
|| smsFallbackContent?.templateContent
|
|
884
|
+
|| smsFallbackContent?.content
|
|
885
|
+
|| '';
|
|
886
|
+
if (channel !== CHANNELS.RCS || !String(fallbackBodyForLiquidPreview).trim()) return;
|
|
887
|
+
try {
|
|
888
|
+
const smsFallbackPayload = preparePreviewPayload(
|
|
889
|
+
CHANNELS.SMS,
|
|
890
|
+
formData || {},
|
|
891
|
+
fallbackBodyForLiquidPreview,
|
|
892
|
+
customValuesForResolve,
|
|
893
|
+
selectedCustomerObj
|
|
894
|
+
);
|
|
895
|
+
const fallbackResponse = await Api.updateEmailPreview(smsFallbackPayload);
|
|
896
|
+
const fallbackPreview = extractPreviewFromLiquidResponse(fallbackResponse);
|
|
897
|
+
setSmsFallbackPreviewText(
|
|
898
|
+
typeof fallbackPreview?.resolvedBody === 'string'
|
|
899
|
+
? fallbackPreview.resolvedBody
|
|
900
|
+
: undefined
|
|
901
|
+
);
|
|
902
|
+
} catch (e) {
|
|
903
|
+
/* keep existing smsFallbackPreviewText on failure */
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
|
|
625
907
|
/**
|
|
626
908
|
* Prepare payload for tag extraction based on channel
|
|
627
909
|
*/
|
|
@@ -669,12 +951,6 @@ const CommonTestAndPreview = (props) => {
|
|
|
669
951
|
templateContent: contentStr,
|
|
670
952
|
};
|
|
671
953
|
|
|
672
|
-
case CHANNELS.WEBPUSH:
|
|
673
|
-
return {
|
|
674
|
-
templateSubject: formDataObj?.content?.title || '',
|
|
675
|
-
templateContent: contentStr,
|
|
676
|
-
};
|
|
677
|
-
|
|
678
954
|
case CHANNELS.ZALO: {
|
|
679
955
|
// For Zalo, extract content from templateListParams array
|
|
680
956
|
// Combine all variable values into a single string for tag extraction
|
|
@@ -722,7 +998,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
722
998
|
} = carousel || {};
|
|
723
999
|
const buttonData = buttons.map((button, index) => {
|
|
724
1000
|
const {
|
|
725
|
-
type, text, phone_number, urlType, url,
|
|
1001
|
+
type, text, phone_number: phoneNumber, urlType, url,
|
|
726
1002
|
} = button || {};
|
|
727
1003
|
const buttonObj = {
|
|
728
1004
|
type,
|
|
@@ -730,7 +1006,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
730
1006
|
index,
|
|
731
1007
|
};
|
|
732
1008
|
if (type === PHONE_NUMBER) {
|
|
733
|
-
buttonObj.phoneNumber =
|
|
1009
|
+
buttonObj.phoneNumber = phoneNumber;
|
|
734
1010
|
}
|
|
735
1011
|
if (type === URL) {
|
|
736
1012
|
buttonObj.url = url;
|
|
@@ -759,7 +1035,133 @@ const CommonTestAndPreview = (props) => {
|
|
|
759
1035
|
};
|
|
760
1036
|
});
|
|
761
1037
|
|
|
762
|
-
|
|
1038
|
+
/**
|
|
1039
|
+
* Build createMessageMeta payload for RCS (test message).
|
|
1040
|
+
* rcsMessageContent: { channel, accountId?, rcsRichCardContent: { contentType, cardType, cardSettings, cardContent }, smsFallBackContent? }
|
|
1041
|
+
* Then rcsDeliverySettings, executionParams, clientName last.
|
|
1042
|
+
*/
|
|
1043
|
+
const buildRcsTestMessagePayload = (
|
|
1044
|
+
creativeFormData,
|
|
1045
|
+
_unusedEditorContentString,
|
|
1046
|
+
_customValuesObj,
|
|
1047
|
+
deliverySettingsOverride,
|
|
1048
|
+
basePayload,
|
|
1049
|
+
_rcsTestMetaExtras = {},
|
|
1050
|
+
) => {
|
|
1051
|
+
const rcsSectionFromForm =
|
|
1052
|
+
creativeFormData?.versions?.base?.content?.RCS ?? creativeFormData?.content?.RCS ?? {};
|
|
1053
|
+
const rcsContentFromForm = rcsSectionFromForm?.rcsContent || {};
|
|
1054
|
+
const smsFallbackFromCreativeForm = rcsSectionFromForm?.smsFallBackContent || {};
|
|
1055
|
+
let rcsCardPayloadList = [];
|
|
1056
|
+
if (Array.isArray(rcsContentFromForm?.cardContent)) {
|
|
1057
|
+
rcsCardPayloadList = rcsContentFromForm.cardContent;
|
|
1058
|
+
} else if (rcsContentFromForm?.cardContent) {
|
|
1059
|
+
rcsCardPayloadList = [rcsContentFromForm.cardContent];
|
|
1060
|
+
}
|
|
1061
|
+
// Raw title/description with template tags; SMS fallback uses tagged template fields (pickFirst…).
|
|
1062
|
+
const cardContentForTestMetaApi = rcsCardPayloadList.map((singleRcsCardPayload) => {
|
|
1063
|
+
const normalizedCardMediaForTestApi = singleRcsCardPayload?.media
|
|
1064
|
+
? normalizeRcsTestCardMedia(singleRcsCardPayload.media)
|
|
1065
|
+
: undefined;
|
|
1066
|
+
const suggestionsFromCard = Array.isArray(singleRcsCardPayload?.suggestions)
|
|
1067
|
+
? singleRcsCardPayload.suggestions
|
|
1068
|
+
: [];
|
|
1069
|
+
const suggestionsFormattedForTestMeta = suggestionsFromCard.map((suggestionItem, index) =>
|
|
1070
|
+
mapRcsSuggestionForTestMeta(suggestionItem, index));
|
|
1071
|
+
return {
|
|
1072
|
+
title: singleRcsCardPayload?.title ?? '',
|
|
1073
|
+
description: singleRcsCardPayload?.description ?? '',
|
|
1074
|
+
mediaType: singleRcsCardPayload?.mediaType ?? MEDIA_TYPE_TEXT,
|
|
1075
|
+
...(normalizedCardMediaForTestApi && { media: normalizedCardMediaForTestApi }),
|
|
1076
|
+
...(suggestionsFormattedForTestMeta.length > 0 && {
|
|
1077
|
+
suggestions: suggestionsFormattedForTestMeta,
|
|
1078
|
+
}),
|
|
1079
|
+
};
|
|
1080
|
+
});
|
|
1081
|
+
// Use the component-level smsFallbackContent prop (has rcsSmsFallbackVarMapped) so DLT
|
|
1082
|
+
// {#var#} slots are converted to {{tagName}} mustache tags before sending to createMessageMeta.
|
|
1083
|
+
const smsFallbackTaggedTemplateBody =
|
|
1084
|
+
getSmsFallbackTextForTagExtraction(smsFallbackContent)
|
|
1085
|
+
|| pickFirstSmsFallbackTemplateString(smsFallbackFromCreativeForm)
|
|
1086
|
+
|| '';
|
|
1087
|
+
const smsSenderFromDelivery = deliverySettingsOverride?.cdmaSenderId?.includes('|')
|
|
1088
|
+
? deliverySettingsOverride.cdmaSenderId.split('|')[1]
|
|
1089
|
+
: deliverySettingsOverride?.cdmaSenderId;
|
|
1090
|
+
const deliveryFallbackSmsId =
|
|
1091
|
+
typeof smsSenderFromDelivery === 'string' ? smsSenderFromDelivery.trim() : '';
|
|
1092
|
+
const creativeFallbackSmsId =
|
|
1093
|
+
smsFallbackFromCreativeForm?.senderId != null
|
|
1094
|
+
? String(smsFallbackFromCreativeForm.senderId).trim()
|
|
1095
|
+
: '';
|
|
1096
|
+
const fallbackSmsSenderIdForChannel = deliveryFallbackSmsId || creativeFallbackSmsId || '';
|
|
1097
|
+
|
|
1098
|
+
const smsFallBackContent =
|
|
1099
|
+
smsFallbackTaggedTemplateBody.trim() !== ''
|
|
1100
|
+
? { message: smsFallbackTaggedTemplateBody }
|
|
1101
|
+
: undefined;
|
|
1102
|
+
|
|
1103
|
+
// accountId: WeCRM account id (not sourceAccountIdentifier) for createMessageMeta
|
|
1104
|
+
const accountIdForMeta =
|
|
1105
|
+
rcsContentFromForm?.accountId != null && String(rcsContentFromForm.accountId).trim() !== ''
|
|
1106
|
+
? String(rcsContentFromForm.accountId)
|
|
1107
|
+
: undefined;
|
|
1108
|
+
|
|
1109
|
+
const rcsRichCardContent = {
|
|
1110
|
+
contentType: RCS_TEST_META_CONTENT_TYPE_RICHCARD,
|
|
1111
|
+
cardType: rcsContentFromForm?.cardType ?? RCS_TEST_META_CARD_TYPE_STANDALONE,
|
|
1112
|
+
cardSettings: rcsContentFromForm?.cardSettings ?? {
|
|
1113
|
+
cardOrientation: RCS_TEST_META_CARD_ORIENTATION_VERTICAL,
|
|
1114
|
+
cardWidth: RCS_TEST_META_CARD_WIDTH_SMALL,
|
|
1115
|
+
},
|
|
1116
|
+
...(cardContentForTestMetaApi.length > 0 && { cardContent: cardContentForTestMetaApi }),
|
|
1117
|
+
};
|
|
1118
|
+
|
|
1119
|
+
const rcsMessageContent = {
|
|
1120
|
+
channel: CHANNELS.RCS,
|
|
1121
|
+
...(accountIdForMeta && { accountId: accountIdForMeta }),
|
|
1122
|
+
rcsRichCardContent,
|
|
1123
|
+
...(smsFallBackContent && { smsFallBackContent }),
|
|
1124
|
+
};
|
|
1125
|
+
const rcsComposite = deliverySettingsOverride?.gsmSenderId ?? '';
|
|
1126
|
+
const [rcsDomainId, rcsSenderId] = rcsComposite.includes('|') ? rcsComposite.split('|') : ['', rcsComposite];
|
|
1127
|
+
const rcsDeliverySettings = {
|
|
1128
|
+
channelSettings: {
|
|
1129
|
+
channel: CHANNELS.RCS,
|
|
1130
|
+
rcsSender: (rcsSenderId || deliverySettingsOverride?.rcsSender) ?? '',
|
|
1131
|
+
domainId:
|
|
1132
|
+
rcsDomainId !== '' && rcsDomainId !== undefined && !Number.isNaN(Number(rcsDomainId))
|
|
1133
|
+
? Number(rcsDomainId)
|
|
1134
|
+
: (deliverySettingsOverride?.domainId ?? 0),
|
|
1135
|
+
fallbackSmsSenderId: fallbackSmsSenderIdForChannel,
|
|
1136
|
+
},
|
|
1137
|
+
additionalSettings: {
|
|
1138
|
+
useTinyUrl: false,
|
|
1139
|
+
encryptUrl: false,
|
|
1140
|
+
linkTrackingEnabled: false,
|
|
1141
|
+
bypassControlUser: false,
|
|
1142
|
+
userSubscriptionDisabled: false,
|
|
1143
|
+
},
|
|
1144
|
+
};
|
|
1145
|
+
const { clientName: baseClientName = CLIENT_NAME_CREATIVES, ...restBase } = basePayload;
|
|
1146
|
+
return {
|
|
1147
|
+
...restBase,
|
|
1148
|
+
rcsMessageContent,
|
|
1149
|
+
rcsDeliverySettings,
|
|
1150
|
+
executionParams: {},
|
|
1151
|
+
clientName: baseClientName,
|
|
1152
|
+
};
|
|
1153
|
+
};
|
|
1154
|
+
|
|
1155
|
+
const prepareTestMessagePayload = (
|
|
1156
|
+
channelType,
|
|
1157
|
+
formDataObj,
|
|
1158
|
+
contentStr,
|
|
1159
|
+
customValuesObj,
|
|
1160
|
+
recipientDetails,
|
|
1161
|
+
previewDataObj,
|
|
1162
|
+
deliverySettingsOverride,
|
|
1163
|
+
rcsExtra = {},
|
|
1164
|
+
) => {
|
|
763
1165
|
// Base payload structure common to all channels
|
|
764
1166
|
const basePayload = {
|
|
765
1167
|
ouId: -1,
|
|
@@ -840,12 +1242,12 @@ const CommonTestAndPreview = (props) => {
|
|
|
840
1242
|
},
|
|
841
1243
|
smsDeliverySettings: {
|
|
842
1244
|
channelSettings: {
|
|
1245
|
+
channel: CHANNELS.SMS,
|
|
843
1246
|
gsmSenderId: deliverySettingsOverride?.gsmSenderId ?? '',
|
|
844
1247
|
domainId: deliverySettingsOverride?.domainId ?? null,
|
|
845
1248
|
domainGatewayMapId: deliverySettingsOverride?.domainGatewayMapId ?? '',
|
|
846
1249
|
targetNdnc: false,
|
|
847
1250
|
cdmaSenderId: deliverySettingsOverride?.cdmaSenderId ?? '',
|
|
848
|
-
channel: CHANNELS.SMS,
|
|
849
1251
|
},
|
|
850
1252
|
additionalSettings: {
|
|
851
1253
|
useTinyUrl: false,
|
|
@@ -989,7 +1391,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
989
1391
|
return {
|
|
990
1392
|
...basePayload,
|
|
991
1393
|
whatsappMessageContent: {
|
|
992
|
-
messageBody: templateEditorValue || '',
|
|
1394
|
+
messageBody: resolvedMessageBody || templateEditorValue || '',
|
|
993
1395
|
accountId: formDataObj?.accountId || '',
|
|
994
1396
|
sourceAccountIdentifier: sourceAccountIdentifier || formDataObj?.sourceAccountIdentifier || '',
|
|
995
1397
|
accountName: formDataObj?.accountName || '',
|
|
@@ -1014,16 +1416,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
1014
1416
|
}
|
|
1015
1417
|
|
|
1016
1418
|
case CHANNELS.RCS:
|
|
1017
|
-
return
|
|
1018
|
-
...basePayload,
|
|
1019
|
-
rcsMessageContent: {
|
|
1020
|
-
channel: CHANNELS.RCS,
|
|
1021
|
-
messageBody: contentStr,
|
|
1022
|
-
rcsType: additionalProps?.rcsType,
|
|
1023
|
-
rcsImageSrc: formDataObj?.rcsImageSrc,
|
|
1024
|
-
rcsSuggestions: formDataObj?.rcsSuggestions,
|
|
1025
|
-
},
|
|
1026
|
-
};
|
|
1419
|
+
return buildRcsTestMessagePayload(formDataObj, contentStr, customValuesObj, deliverySettingsOverride, basePayload, rcsExtra);
|
|
1027
1420
|
|
|
1028
1421
|
case CHANNELS.INAPP: {
|
|
1029
1422
|
// InApp payload structure similar to MobilePush
|
|
@@ -1814,42 +2207,6 @@ const CommonTestAndPreview = (props) => {
|
|
|
1814
2207
|
};
|
|
1815
2208
|
}
|
|
1816
2209
|
|
|
1817
|
-
case CHANNELS.WEBPUSH: {
|
|
1818
|
-
const webpushData = (typeof formDataObj === 'object' && formDataObj !== null)
|
|
1819
|
-
? formDataObj
|
|
1820
|
-
: {};
|
|
1821
|
-
const innerContent = webpushData?.content || {};
|
|
1822
|
-
|
|
1823
|
-
const resolvedTitle = resolveTagsInText(innerContent?.title || '', customValuesObj);
|
|
1824
|
-
const resolvedMessage = resolveTagsInText(innerContent?.message || '', customValuesObj);
|
|
1825
|
-
|
|
1826
|
-
return {
|
|
1827
|
-
...basePayload,
|
|
1828
|
-
webPushMessageContent: {
|
|
1829
|
-
channel: CHANNELS.WEBPUSH,
|
|
1830
|
-
accountId: webpushData?.accountId || null,
|
|
1831
|
-
content: {
|
|
1832
|
-
title: resolvedTitle,
|
|
1833
|
-
message: resolvedMessage,
|
|
1834
|
-
...(innerContent?.iconImageUrl && { iconImageUrl: innerContent.iconImageUrl }),
|
|
1835
|
-
...(innerContent?.cta && { cta: innerContent.cta }),
|
|
1836
|
-
...(innerContent?.expandableDetails && { expandableDetails: innerContent.expandableDetails }),
|
|
1837
|
-
},
|
|
1838
|
-
messageSubject: webpushData?.messageSubject || resolvedTitle || '',
|
|
1839
|
-
},
|
|
1840
|
-
webPushDeliverySettings: {
|
|
1841
|
-
channelSettings: {
|
|
1842
|
-
channel: CHANNELS.WEBPUSH,
|
|
1843
|
-
notificationTtl: {
|
|
1844
|
-
duration: 7,
|
|
1845
|
-
timeUnit: DAYS,
|
|
1846
|
-
},
|
|
1847
|
-
},
|
|
1848
|
-
additionalSettings: {},
|
|
1849
|
-
},
|
|
1850
|
-
};
|
|
1851
|
-
}
|
|
1852
|
-
|
|
1853
2210
|
default:
|
|
1854
2211
|
return basePayload;
|
|
1855
2212
|
}
|
|
@@ -1881,7 +2238,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
1881
2238
|
contentObj = hasPreviewCallBeenMade && previewDataHtml?.resolvedBody
|
|
1882
2239
|
? previewDataHtml.resolvedBody
|
|
1883
2240
|
: getCurrentContent || '';
|
|
1884
|
-
} else if (channel === CHANNELS.WHATSAPP
|
|
2241
|
+
} else if (channel === CHANNELS.WHATSAPP) {
|
|
1885
2242
|
// For WhatsApp, content is an object with templateMsg, media, CTA, etc.
|
|
1886
2243
|
// Content comes from WhatsApp component state, passed via content prop
|
|
1887
2244
|
let resolvedContent = null;
|
|
@@ -2161,6 +2518,10 @@ const CommonTestAndPreview = (props) => {
|
|
|
2161
2518
|
formatMessage,
|
|
2162
2519
|
lastModified: formData?.lastModified,
|
|
2163
2520
|
updatedByName: formData?.updatedByName,
|
|
2521
|
+
smsFallbackContent: isRcsSmsFallbackPreviewEnabled ? smsFallbackContent : null,
|
|
2522
|
+
smsFallbackResolvedText,
|
|
2523
|
+
activePreviewTab,
|
|
2524
|
+
onPreviewTabChange: setActivePreviewTab,
|
|
2164
2525
|
};
|
|
2165
2526
|
};
|
|
2166
2527
|
|
|
@@ -2200,7 +2561,12 @@ const CommonTestAndPreview = (props) => {
|
|
|
2200
2561
|
}, [show, beeInstance, currentTab, channel]);
|
|
2201
2562
|
|
|
2202
2563
|
/**
|
|
2203
|
-
* Initial data load when slidebox opens
|
|
2564
|
+
* Initial data load when slidebox opens.
|
|
2565
|
+
* EXTRACT TAGS CALL SITES (on open/edit RCS):
|
|
2566
|
+
* 1. Here (non-email): actions.extractTagsRequested() at line ~2161 when show is true.
|
|
2567
|
+
* 2. RCS SMS fallback useEffect below: Api.extractTagsWithMetaData() for fallback message.
|
|
2568
|
+
* 3. handleExtractTags() (user clicks "Enter custom values for tags") – not from effects.
|
|
2569
|
+
* The "Process extracted tags" effect only processes API results and must not call extract again.
|
|
2204
2570
|
*/
|
|
2205
2571
|
useEffect(() => {
|
|
2206
2572
|
if (show) {
|
|
@@ -2285,7 +2651,61 @@ const CommonTestAndPreview = (props) => {
|
|
|
2285
2651
|
actions.getTestGroupsRequested();
|
|
2286
2652
|
}
|
|
2287
2653
|
}
|
|
2288
|
-
|
|
2654
|
+
// getCurrentContent: RCS applies cardVarMapped → placeholder resolution; re-extract when it changes.
|
|
2655
|
+
}, [show, beeInstance, currentTab, channel, getCurrentContent]);
|
|
2656
|
+
|
|
2657
|
+
/**
|
|
2658
|
+
* RCS with SMS fallback: extract tags for fallback SMS content as well
|
|
2659
|
+
* (so we can show a separate "Fallback SMS tags" section in left panel).
|
|
2660
|
+
*/
|
|
2661
|
+
useEffect(() => {
|
|
2662
|
+
let cancelled = false;
|
|
2663
|
+
|
|
2664
|
+
if (!show || channel !== CHANNELS.RCS) {
|
|
2665
|
+
return () => {
|
|
2666
|
+
cancelled = true;
|
|
2667
|
+
};
|
|
2668
|
+
}
|
|
2669
|
+
|
|
2670
|
+
if (!smsFallbackContent?.templateContent && !smsFallbackContent?.content) {
|
|
2671
|
+
setSmsFallbackExtractedTags([]);
|
|
2672
|
+
setSmsFallbackRequiredTags([]);
|
|
2673
|
+
setSmsFallbackOptionalTags([]);
|
|
2674
|
+
return () => {
|
|
2675
|
+
cancelled = true;
|
|
2676
|
+
};
|
|
2677
|
+
}
|
|
2678
|
+
|
|
2679
|
+
setIsExtractingSmsFallbackTags(true);
|
|
2680
|
+
(async () => {
|
|
2681
|
+
try {
|
|
2682
|
+
const fallbackBodyForExtractApi = getSmsFallbackTextForTagExtraction(smsFallbackContent);
|
|
2683
|
+
const payload = {
|
|
2684
|
+
messageTitle: '',
|
|
2685
|
+
messageBody: fallbackBodyForExtractApi || '',
|
|
2686
|
+
};
|
|
2687
|
+
const response = await Api.extractTagsWithMetaData(payload); //not using saga action here because we dont store fallbacksms related data in store but only in useState since this is only used in RCS SMS fallback
|
|
2688
|
+
let smsFallbackTagTree = response?.data ?? [];
|
|
2689
|
+
if (!Array.isArray(smsFallbackTagTree)) smsFallbackTagTree = [];
|
|
2690
|
+
if (!smsTemplateHasMustacheTags(fallbackBodyForExtractApi)) {
|
|
2691
|
+
smsFallbackTagTree = [];
|
|
2692
|
+
} else if (smsFallbackTagTree.length === 0) {
|
|
2693
|
+
smsFallbackTagTree = buildSyntheticSmsMustacheTags(fallbackBodyForExtractApi);
|
|
2694
|
+
}
|
|
2695
|
+
if (cancelled) return;
|
|
2696
|
+
setSmsFallbackExtractedTags(smsFallbackTagTree);
|
|
2697
|
+
} catch (e) {
|
|
2698
|
+
if (cancelled) return;
|
|
2699
|
+
setSmsFallbackExtractedTags([]);
|
|
2700
|
+
} finally {
|
|
2701
|
+
if (!cancelled) setIsExtractingSmsFallbackTags(false);
|
|
2702
|
+
}
|
|
2703
|
+
})();
|
|
2704
|
+
|
|
2705
|
+
return () => {
|
|
2706
|
+
cancelled = true;
|
|
2707
|
+
};
|
|
2708
|
+
}, [show, channel, smsFallbackContent]);
|
|
2289
2709
|
|
|
2290
2710
|
/**
|
|
2291
2711
|
* Email-specific: Handle content updates for both BEE and CKEditor
|
|
@@ -2337,15 +2757,22 @@ const CommonTestAndPreview = (props) => {
|
|
|
2337
2757
|
setSelectedCustomer(null);
|
|
2338
2758
|
setRequiredTags([]);
|
|
2339
2759
|
setOptionalTags([]);
|
|
2760
|
+
setSmsFallbackExtractedTags([]);
|
|
2761
|
+
setSmsFallbackRequiredTags([]);
|
|
2762
|
+
setSmsFallbackOptionalTags([]);
|
|
2763
|
+
setIsExtractingSmsFallbackTags(false);
|
|
2340
2764
|
setCustomValues({});
|
|
2341
2765
|
setShowJSON(false);
|
|
2342
2766
|
setTagsExtracted(false);
|
|
2343
2767
|
setPreviewDevice(DESKTOP);
|
|
2768
|
+
setActivePreviewTab(PREVIEW_TAB_RCS);
|
|
2769
|
+
setSmsFallbackPreviewText(undefined);
|
|
2344
2770
|
setSelectedTestEntities([]);
|
|
2345
2771
|
actions.clearPrefilledValues();
|
|
2346
2772
|
} else {
|
|
2347
2773
|
// Reset device to initialDevice when opening (Android for mobile channels, Desktop for others)
|
|
2348
2774
|
setPreviewDevice(initialDevice);
|
|
2775
|
+
setActivePreviewTab(PREVIEW_TAB_RCS);
|
|
2349
2776
|
}
|
|
2350
2777
|
}, [show, initialDevice]);
|
|
2351
2778
|
|
|
@@ -2354,79 +2781,10 @@ const CommonTestAndPreview = (props) => {
|
|
|
2354
2781
|
*/
|
|
2355
2782
|
useEffect(() => {
|
|
2356
2783
|
if (previewData) {
|
|
2357
|
-
setPreviewDataHtml(previewData);
|
|
2784
|
+
setPreviewDataHtml(toPlainPreviewData(previewData));
|
|
2358
2785
|
}
|
|
2359
2786
|
}, [previewData]);
|
|
2360
2787
|
|
|
2361
|
-
/**
|
|
2362
|
-
* Process extracted tags and categorize them
|
|
2363
|
-
*/
|
|
2364
|
-
useEffect(() => {
|
|
2365
|
-
// Categorize tags into required and optional
|
|
2366
|
-
const required = [];
|
|
2367
|
-
const optional = [];
|
|
2368
|
-
let hasPersonalizationTags = false;
|
|
2369
|
-
|
|
2370
|
-
if (extractedTags?.length > 0) {
|
|
2371
|
-
const processTag = (tag, parentPath = '') => {
|
|
2372
|
-
const currentPath = parentPath ? `${parentPath}.${tag.name}` : tag.name;
|
|
2373
|
-
|
|
2374
|
-
// Skip unsubscribe tag for input fields
|
|
2375
|
-
if (tag?.name === UNSUBSCRIBE_TAG_NAME) {
|
|
2376
|
-
return;
|
|
2377
|
-
}
|
|
2378
|
-
|
|
2379
|
-
hasPersonalizationTags = true;
|
|
2380
|
-
|
|
2381
|
-
if (tag?.metaData?.userDriven === false) {
|
|
2382
|
-
required.push({
|
|
2383
|
-
...tag,
|
|
2384
|
-
fullPath: currentPath,
|
|
2385
|
-
});
|
|
2386
|
-
} else if (tag?.metaData?.userDriven === true) {
|
|
2387
|
-
optional.push({
|
|
2388
|
-
...tag,
|
|
2389
|
-
fullPath: currentPath,
|
|
2390
|
-
});
|
|
2391
|
-
}
|
|
2392
|
-
|
|
2393
|
-
if (tag?.children?.length > 0) {
|
|
2394
|
-
tag.children.forEach((child) => processTag(child, currentPath));
|
|
2395
|
-
}
|
|
2396
|
-
};
|
|
2397
|
-
|
|
2398
|
-
extractedTags.forEach((tag) => processTag(tag));
|
|
2399
|
-
|
|
2400
|
-
if (hasPersonalizationTags) {
|
|
2401
|
-
setRequiredTags(required);
|
|
2402
|
-
setOptionalTags(optional);
|
|
2403
|
-
setTagsExtracted(true); // Mark tags as extracted and processed
|
|
2404
|
-
|
|
2405
|
-
// Initialize custom values for required tags
|
|
2406
|
-
const initialValues = {};
|
|
2407
|
-
required.forEach((tag) => {
|
|
2408
|
-
initialValues[tag?.fullPath] = '';
|
|
2409
|
-
});
|
|
2410
|
-
optional.forEach((tag) => {
|
|
2411
|
-
initialValues[tag?.fullPath] = '';
|
|
2412
|
-
});
|
|
2413
|
-
setCustomValues(initialValues);
|
|
2414
|
-
} else {
|
|
2415
|
-
// Reset all tag-related state if no personalization tags
|
|
2416
|
-
setRequiredTags([]);
|
|
2417
|
-
setOptionalTags([]);
|
|
2418
|
-
setCustomValues({});
|
|
2419
|
-
setTagsExtracted(false);
|
|
2420
|
-
}
|
|
2421
|
-
} else {
|
|
2422
|
-
// Reset all tag-related state if no tags
|
|
2423
|
-
setRequiredTags([]);
|
|
2424
|
-
setOptionalTags([]);
|
|
2425
|
-
setCustomValues({});
|
|
2426
|
-
setTagsExtracted(false);
|
|
2427
|
-
}
|
|
2428
|
-
}, [extractedTags]);
|
|
2429
|
-
|
|
2430
2788
|
/**
|
|
2431
2789
|
* Handle customer selection and fetch prefilled values
|
|
2432
2790
|
*/
|
|
@@ -2434,17 +2792,15 @@ const CommonTestAndPreview = (props) => {
|
|
|
2434
2792
|
if (selectedCustomer && config.enableCustomerSearch !== false) {
|
|
2435
2793
|
setTagsExtracted(true); // Auto-open custom values editor
|
|
2436
2794
|
|
|
2437
|
-
// Get all available tags
|
|
2438
|
-
const allTags = [...requiredTags, ...optionalTags];
|
|
2439
2795
|
const requiredTagObj = {};
|
|
2440
|
-
|
|
2796
|
+
allRequiredTags.forEach((tag) => {
|
|
2441
2797
|
requiredTagObj[tag?.fullPath] = '';
|
|
2442
2798
|
});
|
|
2443
2799
|
if (allTags.length > 0) {
|
|
2444
2800
|
const payload = preparePreviewPayload(
|
|
2445
|
-
|
|
2801
|
+
activeChannelForActions,
|
|
2446
2802
|
formData || {},
|
|
2447
|
-
|
|
2803
|
+
activeContentForActions,
|
|
2448
2804
|
{
|
|
2449
2805
|
...requiredTagObj,
|
|
2450
2806
|
},
|
|
@@ -2453,7 +2809,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
2453
2809
|
actions.getPrefilledValuesRequested(payload);
|
|
2454
2810
|
}
|
|
2455
2811
|
}
|
|
2456
|
-
}, [selectedCustomer]);
|
|
2812
|
+
}, [selectedCustomer, allTags.length, activeChannelForActions, activePreviewTab]);
|
|
2457
2813
|
|
|
2458
2814
|
/**
|
|
2459
2815
|
* Update custom values with prefilled values from API
|
|
@@ -2464,24 +2820,29 @@ const CommonTestAndPreview = (props) => {
|
|
|
2464
2820
|
if (prefilledValues && selectedCustomer) {
|
|
2465
2821
|
// Always replace all values with prefilled values
|
|
2466
2822
|
const updatedValues = {};
|
|
2467
|
-
|
|
2823
|
+
allTags.forEach((tag) => {
|
|
2468
2824
|
updatedValues[tag?.fullPath] = prefilledValues[tag?.fullPath] || '';
|
|
2469
2825
|
});
|
|
2470
2826
|
|
|
2471
2827
|
setCustomValues(updatedValues);
|
|
2472
2828
|
|
|
2473
2829
|
// Update preview with prefilled values (this is a valid preview call trigger)
|
|
2830
|
+
// For RCS: always dispatch with RCS channel/content so previewDataHtml stays RCS-shaped.
|
|
2831
|
+
// SMS fallback preview is kept in sync by syncSmsFallbackPreview via smsFallbackPreviewText.
|
|
2832
|
+
const previewChannelForPrefill = channel === CHANNELS.RCS ? CHANNELS.RCS : activeChannelForActions;
|
|
2833
|
+
const previewContentForPrefill = channel === CHANNELS.RCS ? getCurrentContent : activeContentForActions;
|
|
2474
2834
|
const payload = preparePreviewPayload(
|
|
2475
|
-
|
|
2835
|
+
previewChannelForPrefill,
|
|
2476
2836
|
formData || {},
|
|
2477
|
-
|
|
2837
|
+
previewContentForPrefill,
|
|
2478
2838
|
updatedValues,
|
|
2479
2839
|
selectedCustomer
|
|
2480
2840
|
);
|
|
2481
2841
|
actions.updatePreviewRequested(payload);
|
|
2482
2842
|
setHasPreviewCallBeenMade(true); // Mark that preview call was made
|
|
2843
|
+
void syncSmsFallbackPreview(updatedValues, selectedCustomer);
|
|
2483
2844
|
}
|
|
2484
|
-
}, [JSON.stringify(prefilledValues), selectedCustomer]);
|
|
2845
|
+
}, [JSON.stringify(prefilledValues), selectedCustomer, activeChannelForActions, activePreviewTab]);
|
|
2485
2846
|
|
|
2486
2847
|
/**
|
|
2487
2848
|
* Map channel constants to display names (lowercase for message)
|
|
@@ -2575,11 +2936,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
2575
2936
|
setTagsExtracted(true); // Auto-open custom values editor
|
|
2576
2937
|
|
|
2577
2938
|
// Clear any existing values while waiting for prefilled values
|
|
2578
|
-
|
|
2579
|
-
[...requiredTags, ...optionalTags].forEach((tag) => {
|
|
2580
|
-
emptyValues[tag?.fullPath] = '';
|
|
2581
|
-
});
|
|
2582
|
-
setCustomValues(emptyValues);
|
|
2939
|
+
setCustomValues(buildEmptyValues());
|
|
2583
2940
|
};
|
|
2584
2941
|
|
|
2585
2942
|
/**
|
|
@@ -2593,11 +2950,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
2593
2950
|
actions.clearPreviewErrors();
|
|
2594
2951
|
|
|
2595
2952
|
// Initialize empty values for all tags
|
|
2596
|
-
|
|
2597
|
-
[...requiredTags, ...optionalTags].forEach((tag) => {
|
|
2598
|
-
emptyValues[tag?.fullPath] = '';
|
|
2599
|
-
});
|
|
2600
|
-
setCustomValues(emptyValues);
|
|
2953
|
+
setCustomValues(buildEmptyValues());
|
|
2601
2954
|
|
|
2602
2955
|
// Don't make preview call when clearing selection - just reset to raw content
|
|
2603
2956
|
// Preview will be shown using raw formData/content
|
|
@@ -2633,17 +2986,20 @@ const CommonTestAndPreview = (props) => {
|
|
|
2633
2986
|
*/
|
|
2634
2987
|
const handleDiscardCustomValues = () => {
|
|
2635
2988
|
// Initialize empty values for all tags
|
|
2636
|
-
const emptyValues =
|
|
2637
|
-
[...requiredTags, ...optionalTags].forEach((tag) => {
|
|
2638
|
-
emptyValues[tag?.fullPath] = '';
|
|
2639
|
-
});
|
|
2989
|
+
const emptyValues = buildEmptyValues();
|
|
2640
2990
|
setCustomValues(emptyValues);
|
|
2641
2991
|
|
|
2992
|
+
// Reset SMS fallback preview so it shows raw template (with {{tags}} visible) after discard
|
|
2993
|
+
setSmsFallbackPreviewText(undefined);
|
|
2994
|
+
|
|
2642
2995
|
// Update preview with empty values (this is a valid preview call trigger)
|
|
2996
|
+
// For RCS: always dispatch with RCS channel/content so previewDataHtml stays RCS-shaped.
|
|
2997
|
+
const previewChannelForDiscard = channel === CHANNELS.RCS ? CHANNELS.RCS : activeChannelForActions;
|
|
2998
|
+
const previewContentForDiscard = channel === CHANNELS.RCS ? getCurrentContent : activeContentForActions;
|
|
2643
2999
|
const payload = preparePreviewPayload(
|
|
2644
|
-
|
|
3000
|
+
previewChannelForDiscard,
|
|
2645
3001
|
formData || {},
|
|
2646
|
-
|
|
3002
|
+
previewContentForDiscard,
|
|
2647
3003
|
emptyValues,
|
|
2648
3004
|
selectedCustomer
|
|
2649
3005
|
);
|
|
@@ -2657,14 +3013,21 @@ const CommonTestAndPreview = (props) => {
|
|
|
2657
3013
|
*/
|
|
2658
3014
|
const handleUpdatePreview = async () => {
|
|
2659
3015
|
try {
|
|
3016
|
+
// For RCS: always dispatch with RCS channel/content so previewDataHtml stays RCS-shaped,
|
|
3017
|
+
// even when the user triggers update from the SMS fallback tab.
|
|
3018
|
+
// SMS fallback preview is kept in sync by syncSmsFallbackPreview via smsFallbackPreviewText.
|
|
3019
|
+
const previewChannel = channel === CHANNELS.RCS ? CHANNELS.RCS : activeChannelForActions;
|
|
3020
|
+
const previewContent = channel === CHANNELS.RCS ? getCurrentContent : activeContentForActions;
|
|
2660
3021
|
const payload = preparePreviewPayload(
|
|
2661
|
-
|
|
3022
|
+
previewChannel,
|
|
2662
3023
|
formData || {},
|
|
2663
|
-
|
|
3024
|
+
previewContent,
|
|
2664
3025
|
customValues,
|
|
2665
3026
|
selectedCustomer
|
|
2666
3027
|
);
|
|
2667
3028
|
await actions.updatePreviewRequested(payload);
|
|
3029
|
+
|
|
3030
|
+
await syncSmsFallbackPreview(customValues, selectedCustomer);
|
|
2668
3031
|
setHasPreviewCallBeenMade(true); // Mark that preview call was made
|
|
2669
3032
|
} catch (error) {
|
|
2670
3033
|
CapNotification.error({
|
|
@@ -2674,25 +3037,115 @@ const CommonTestAndPreview = (props) => {
|
|
|
2674
3037
|
};
|
|
2675
3038
|
|
|
2676
3039
|
/**
|
|
2677
|
-
*
|
|
3040
|
+
* Categorize extracted tags into required/optional.
|
|
2678
3041
|
*/
|
|
2679
|
-
const
|
|
2680
|
-
|
|
2681
|
-
|
|
3042
|
+
const categorizeTags = (tagsTree = []) => {
|
|
3043
|
+
const required = [];
|
|
3044
|
+
const optional = [];
|
|
3045
|
+
let hasPersonalizationTags = false;
|
|
3046
|
+
const processTag = (tag, parentPath = '') => {
|
|
3047
|
+
const currentPath = parentPath ? `${parentPath}.${tag.name}` : tag.name;
|
|
3048
|
+
|
|
3049
|
+
// Skip unsubscribe tag for input fields
|
|
3050
|
+
if (tag?.name === UNSUBSCRIBE_TAG_NAME) return;
|
|
3051
|
+
|
|
3052
|
+
hasPersonalizationTags = true;
|
|
3053
|
+
const userDriven = tag?.metaData?.userDriven;
|
|
3054
|
+
if (userDriven === true) {
|
|
3055
|
+
optional.push({ ...tag, fullPath: currentPath });
|
|
3056
|
+
} else {
|
|
3057
|
+
// false or missing (SMS/DLT extract often omits metaData) → required for test values
|
|
3058
|
+
required.push({ ...tag, fullPath: currentPath });
|
|
3059
|
+
}
|
|
3060
|
+
|
|
3061
|
+
if (tag?.children?.length > 0) {
|
|
3062
|
+
tag.children.forEach((child) => processTag(child, currentPath));
|
|
3063
|
+
}
|
|
3064
|
+
};
|
|
3065
|
+
|
|
3066
|
+
(tagsTree || []).forEach((tag) => processTag(tag));
|
|
3067
|
+
return { required, optional, hasPersonalizationTags };
|
|
3068
|
+
};
|
|
3069
|
+
|
|
3070
|
+
/**
|
|
3071
|
+
* Apply tag extraction when content comes from RCS + SMS fallback (no API call).
|
|
3072
|
+
*/
|
|
3073
|
+
const applyRcsSmsFallbackTagExtraction = () => {
|
|
3074
|
+
const rcsPrimaryCategorized = categorizeTags(extractedTags ?? []);
|
|
3075
|
+
const fallbackSmsResolvedForTags = smsFallbackTextForTagExtraction ?? '';
|
|
3076
|
+
let fallbackSmsTagTree = smsFallbackExtractedTags?.length > 0
|
|
3077
|
+
? smsFallbackExtractedTags
|
|
3078
|
+
: buildSyntheticSmsMustacheTags(fallbackSmsResolvedForTags);
|
|
3079
|
+
if (!smsTemplateHasMustacheTags(fallbackSmsResolvedForTags)) {
|
|
3080
|
+
fallbackSmsTagTree = [];
|
|
3081
|
+
}
|
|
3082
|
+
const fallbackSmsCategorized = categorizeTags(fallbackSmsTagTree);
|
|
3083
|
+
setRequiredTags(rcsPrimaryCategorized.required);
|
|
3084
|
+
setOptionalTags(rcsPrimaryCategorized.optional);
|
|
3085
|
+
setSmsFallbackRequiredTags(fallbackSmsCategorized.required);
|
|
3086
|
+
setSmsFallbackOptionalTags(fallbackSmsCategorized.optional);
|
|
3087
|
+
setTagsExtracted(
|
|
3088
|
+
rcsPrimaryCategorized.hasPersonalizationTags || fallbackSmsCategorized.hasPersonalizationTags,
|
|
3089
|
+
);
|
|
3090
|
+
setCustomValues((prev) => mergeCustomValuesWithTagKeys(prev, rcsPrimaryCategorized, fallbackSmsCategorized));
|
|
3091
|
+
};
|
|
2682
3092
|
|
|
3093
|
+
/**
|
|
3094
|
+
* When extract-tags API returns, map Redux `extractedTags` into required/optional so
|
|
3095
|
+
* CustomValuesEditor shows personalization fields (effect was previously commented out).
|
|
3096
|
+
* RCS + SMS fallback: merge primary + fallback tag trees when fallback template exists.
|
|
3097
|
+
*/
|
|
3098
|
+
useEffect(() => {
|
|
3099
|
+
if (!show) return;
|
|
3100
|
+
const hasFallbackSmsTemplate = !!(smsFallbackContent?.templateContent || smsFallbackContent?.content);
|
|
3101
|
+
if (channel === CHANNELS.RCS && hasFallbackSmsTemplate) {
|
|
3102
|
+
applyRcsSmsFallbackTagExtraction();
|
|
3103
|
+
return;
|
|
3104
|
+
}
|
|
3105
|
+
const smsEditorBody = typeof getCurrentContent === 'string' ? getCurrentContent : '';
|
|
3106
|
+
let smsTagSource = channel === CHANNELS.SMS && (!extractedTags || extractedTags.length === 0)
|
|
3107
|
+
? buildSyntheticSmsMustacheTags(smsEditorBody)
|
|
3108
|
+
: (extractedTags ?? []);
|
|
3109
|
+
if (channel === CHANNELS.SMS && !smsTemplateHasMustacheTags(smsEditorBody)) {
|
|
3110
|
+
smsTagSource = [];
|
|
3111
|
+
}
|
|
3112
|
+
const { required, optional, hasPersonalizationTags } = categorizeTags(smsTagSource);
|
|
3113
|
+
setRequiredTags(required);
|
|
3114
|
+
setOptionalTags(optional);
|
|
3115
|
+
setTagsExtracted(hasPersonalizationTags);
|
|
3116
|
+
setCustomValues((prev) => mergeCustomValuesWithTagKeys(prev, { required, optional }, { required: [], optional: [] }));
|
|
3117
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- applyRcsSmsFallbackTagExtraction closes over latest extractedTags/smsFallbackExtractedTags
|
|
3118
|
+
}, [show, extractedTags, channel, smsFallbackContent, smsFallbackExtractedTags, getCurrentContent, smsFallbackTextForTagExtraction]);
|
|
3119
|
+
|
|
3120
|
+
/**
|
|
3121
|
+
* Get content to run tag extraction on (channel-specific).
|
|
3122
|
+
*/
|
|
3123
|
+
const getContentForTagExtraction = () => {
|
|
3124
|
+
let contentToExtract = activeContentForActions;
|
|
2683
3125
|
if (channel === CHANNELS.EMAIL && formData) {
|
|
2684
3126
|
const currentTabData = formData[currentTab - 1];
|
|
2685
3127
|
const activeTab = currentTabData?.activeTab;
|
|
2686
3128
|
const templateContent = currentTabData?.[activeTab]?.['template-content'];
|
|
2687
3129
|
contentToExtract = templateContent || contentToExtract;
|
|
2688
3130
|
}
|
|
3131
|
+
return contentToExtract;
|
|
3132
|
+
};
|
|
3133
|
+
|
|
3134
|
+
/**
|
|
3135
|
+
* Handle extract tags
|
|
3136
|
+
*/
|
|
3137
|
+
const handleExtractTags = () => {
|
|
3138
|
+
if (channel === CHANNELS.RCS) {
|
|
3139
|
+
applyRcsSmsFallbackTagExtraction();
|
|
3140
|
+
return;
|
|
3141
|
+
}
|
|
2689
3142
|
|
|
2690
|
-
|
|
3143
|
+
const contentToExtract = getContentForTagExtraction();
|
|
2691
3144
|
const tags = contentToExtract.match(/{{[^}]+}}/g) || [];
|
|
2692
3145
|
const hasPersonalizationTags = tags.some((tag) => !tag.includes(UNSUBSCRIBE_TAG_NAME));
|
|
3146
|
+
const onlyUnsubscribe = !hasPersonalizationTags && tags.length === 1 && tags[0].includes(UNSUBSCRIBE_TAG_NAME);
|
|
2693
3147
|
|
|
2694
|
-
if (
|
|
2695
|
-
// If only unsubscribe tag is present, show noTagsExtracted message
|
|
3148
|
+
if (onlyUnsubscribe) {
|
|
2696
3149
|
setTagsExtracted(false);
|
|
2697
3150
|
setRequiredTags([]);
|
|
2698
3151
|
setOptionalTags([]);
|
|
@@ -2700,10 +3153,9 @@ const CommonTestAndPreview = (props) => {
|
|
|
2700
3153
|
return;
|
|
2701
3154
|
}
|
|
2702
3155
|
|
|
2703
|
-
// Extract tags
|
|
2704
3156
|
setTagsExtracted(true);
|
|
2705
3157
|
const { templateSubject, templateContent } = prepareTagExtractionPayload(
|
|
2706
|
-
|
|
3158
|
+
activeChannelForActions,
|
|
2707
3159
|
formData || {},
|
|
2708
3160
|
contentToExtract
|
|
2709
3161
|
);
|
|
@@ -2765,7 +3217,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
2765
3217
|
if (existingTestCustomer) {
|
|
2766
3218
|
const entityId = existingTestCustomer.userId ?? existingTestCustomer.customerId;
|
|
2767
3219
|
if (entityId != null) {
|
|
2768
|
-
const id =
|
|
3220
|
+
const id = normalizeTestEntityId(entityId);
|
|
2769
3221
|
setSelectedTestEntities((prev) => (
|
|
2770
3222
|
prev.some((existing) => testEntityIdsEqual(existing, id)) ? prev : [...prev, id]
|
|
2771
3223
|
));
|
|
@@ -2802,7 +3254,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
2802
3254
|
(c) => String(c?.customerId) === customerIdFromLookup || String(c?.userId) === customerIdFromLookup
|
|
2803
3255
|
);
|
|
2804
3256
|
if (alreadyInTestListByCustomerId) {
|
|
2805
|
-
const id =
|
|
3257
|
+
const id = normalizeTestEntityId(customerIdFromLookup);
|
|
2806
3258
|
setSelectedTestEntities((prev) => (
|
|
2807
3259
|
prev.some((existing) => testEntityIdsEqual(existing, id)) ? prev : [...prev, id]
|
|
2808
3260
|
));
|
|
@@ -2839,26 +3291,21 @@ const CommonTestAndPreview = (props) => {
|
|
|
2839
3291
|
const handleSendTestMessage = () => {
|
|
2840
3292
|
const allUserIds = [];
|
|
2841
3293
|
selectedTestEntities.forEach((entityId) => {
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
if (group) {
|
|
2846
|
-
allUserIds.push(...group.userIds);
|
|
2847
|
-
}
|
|
3294
|
+
const group = testGroups.find((g) => testEntityIdsEqual(g.groupId, entityId));
|
|
3295
|
+
if (group) {
|
|
3296
|
+
allUserIds.push(...group.userIds);
|
|
2848
3297
|
} else {
|
|
2849
|
-
|
|
2850
|
-
? String(entityId).slice('customer:'.length)
|
|
2851
|
-
: String(entityId);
|
|
2852
|
-
allUserIds.push(Number(rawId));
|
|
3298
|
+
allUserIds.push(entityId);
|
|
2853
3299
|
}
|
|
2854
3300
|
});
|
|
2855
3301
|
const uniqueUserIds = [...new Set(allUserIds)];
|
|
2856
3302
|
|
|
2857
|
-
const deliveryOverride = [CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP].includes(channel)
|
|
3303
|
+
const deliveryOverride = [CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP, CHANNELS.RCS].includes(channel)
|
|
2858
3304
|
? testPreviewDeliverySettings[channel]
|
|
2859
3305
|
: null;
|
|
2860
3306
|
|
|
2861
|
-
//
|
|
3307
|
+
// createMessageMeta must match the creative channel and full creative (RCS + SMS fallback in one meta).
|
|
3308
|
+
// Do not use activeChannelForActions / activeContentForActions — those follow the RCS vs Fallback SMS *preview* tab.
|
|
2862
3309
|
const initialPayload = prepareTestMessagePayload(
|
|
2863
3310
|
channel,
|
|
2864
3311
|
formData || content || {},
|
|
@@ -2866,7 +3313,8 @@ const CommonTestAndPreview = (props) => {
|
|
|
2866
3313
|
customValues,
|
|
2867
3314
|
uniqueUserIds,
|
|
2868
3315
|
previewData,
|
|
2869
|
-
deliveryOverride
|
|
3316
|
+
deliveryOverride,
|
|
3317
|
+
{},
|
|
2870
3318
|
);
|
|
2871
3319
|
|
|
2872
3320
|
actions.createMessageMetaRequested(
|
|
@@ -2899,11 +3347,10 @@ const CommonTestAndPreview = (props) => {
|
|
|
2899
3347
|
// ============================================
|
|
2900
3348
|
// RENDER HELPER FUNCTIONS
|
|
2901
3349
|
// ============================================
|
|
2902
|
-
|
|
2903
3350
|
const renderLeftPanelContent = () => (
|
|
2904
3351
|
<LeftPanelContent
|
|
2905
|
-
isExtractingTags={isExtractingTags}
|
|
2906
|
-
extractedTags={
|
|
3352
|
+
isExtractingTags={isExtractingTags || isExtractingSmsFallbackTags}
|
|
3353
|
+
extractedTags={leftPanelExtractedTags}
|
|
2907
3354
|
selectedCustomer={selectedCustomer}
|
|
2908
3355
|
handleCustomerSelect={handleCustomerSelect}
|
|
2909
3356
|
handleSearchCustomer={handleSearchCustomer}
|
|
@@ -2921,15 +3368,29 @@ const CommonTestAndPreview = (props) => {
|
|
|
2921
3368
|
|
|
2922
3369
|
const renderCustomValuesEditor = () => (
|
|
2923
3370
|
<CustomValuesEditor
|
|
2924
|
-
isExtractingTags={isExtractingTags}
|
|
3371
|
+
isExtractingTags={isExtractingTags || isExtractingSmsFallbackTags}
|
|
2925
3372
|
isUpdatePreviewDisabled={isUpdatePreviewDisabled}
|
|
2926
3373
|
showJSON={showJSON}
|
|
2927
3374
|
setShowJSON={setShowJSON}
|
|
2928
3375
|
customValues={customValues}
|
|
2929
3376
|
handleJSONTextChange={handleJSONTextChange}
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
3377
|
+
sections={[
|
|
3378
|
+
{
|
|
3379
|
+
key: channel,
|
|
3380
|
+
title:
|
|
3381
|
+
channel === CHANNELS.RCS
|
|
3382
|
+
? messages.rcsTagsSectionTitle
|
|
3383
|
+
: messages[`${channel}TagsSectionTitle`],
|
|
3384
|
+
requiredTags,
|
|
3385
|
+
optionalTags,
|
|
3386
|
+
},
|
|
3387
|
+
{
|
|
3388
|
+
key: PREVIEW_TAB_SMS_FALLBACK,
|
|
3389
|
+
title: isRcsSmsFallbackPreviewEnabled ? messages.smsFallbackTagsSectionTitle : null,
|
|
3390
|
+
requiredTags: smsFallbackRequiredTags,
|
|
3391
|
+
optionalTags: smsFallbackOptionalTags,
|
|
3392
|
+
},
|
|
3393
|
+
]}
|
|
2933
3394
|
handleCustomValueChange={handleCustomValueChange}
|
|
2934
3395
|
handleDiscardCustomValues={handleDiscardCustomValues}
|
|
2935
3396
|
handleUpdatePreview={handleUpdatePreview}
|
|
@@ -2945,18 +3406,13 @@ const CommonTestAndPreview = (props) => {
|
|
|
2945
3406
|
}));
|
|
2946
3407
|
};
|
|
2947
3408
|
|
|
2948
|
-
/** Trim pasted emails (trailing CR/LF).
|
|
3409
|
+
/** Trim pasted emails (trailing CR/LF). Allow any input for SMS; valid-only check is in renderAddTestCustomerButton. */
|
|
2949
3410
|
const handleTestCustomersSearch = useCallback((value) => {
|
|
2950
3411
|
if (value == null || value === '') {
|
|
2951
3412
|
setSearchValue('');
|
|
2952
3413
|
return;
|
|
2953
3414
|
}
|
|
2954
|
-
|
|
2955
|
-
if (channel === CHANNELS.SMS) {
|
|
2956
|
-
setSearchValue(formatPhoneNumber(raw));
|
|
2957
|
-
} else {
|
|
2958
|
-
setSearchValue(raw);
|
|
2959
|
-
}
|
|
3415
|
+
setSearchValue(String(value).trim());
|
|
2960
3416
|
}, [channel]);
|
|
2961
3417
|
|
|
2962
3418
|
const renderSendTestMessage = () => (
|
|
@@ -2974,12 +3430,13 @@ const CommonTestAndPreview = (props) => {
|
|
|
2974
3430
|
renderAddTestCustomerButton={renderAddTestCustomerButton}
|
|
2975
3431
|
formatMessage={formatMessage}
|
|
2976
3432
|
deliverySettings={testPreviewDeliverySettings[channel]}
|
|
2977
|
-
|
|
3433
|
+
senderDetailsByChannel={senderDetailsByChannel}
|
|
2978
3434
|
wecrmAccounts={wecrmAccounts}
|
|
2979
3435
|
onSaveDeliverySettings={handleSaveDeliverySettings}
|
|
2980
3436
|
isLoadingSenderDetails={isLoadingSenderDetails}
|
|
2981
3437
|
smsTraiDltEnabled={smsTraiDltEnabled}
|
|
2982
3438
|
registeredSenderIds={registeredSenderIds}
|
|
3439
|
+
isChannelSmsFallbackPreviewEnabled={isRcsSmsFallbackPreviewEnabled}
|
|
2983
3440
|
searchValue={searchValue}
|
|
2984
3441
|
setSearchValue={handleTestCustomersSearch}
|
|
2985
3442
|
/>
|
|
@@ -2993,14 +3450,13 @@ const CommonTestAndPreview = (props) => {
|
|
|
2993
3450
|
|
|
2994
3451
|
const renderAddTestCustomerButton = () => {
|
|
2995
3452
|
const raw = (searchValue || '').trim();
|
|
2996
|
-
const value = channel === CHANNELS.SMS ? formatPhoneNumber(raw) : raw;
|
|
2997
3453
|
const showAddButton =
|
|
2998
3454
|
[CHANNELS.EMAIL, CHANNELS.SMS].includes(channel) &&
|
|
2999
|
-
(channel === CHANNELS.EMAIL ? isValidEmail(
|
|
3455
|
+
(channel === CHANNELS.EMAIL ? isValidEmail(raw) : isValidMobile(formatPhoneNumber(raw)));
|
|
3000
3456
|
if (!showAddButton) return null;
|
|
3001
3457
|
return (
|
|
3002
3458
|
<AddTestCustomerButton
|
|
3003
|
-
searchValue={
|
|
3459
|
+
searchValue={channel === CHANNELS.SMS ? formatPhoneNumber(raw) : raw}
|
|
3004
3460
|
handleAddTestCustomer={handleAddTestCustomer}
|
|
3005
3461
|
/>
|
|
3006
3462
|
);
|