@capillarytech/creatives-library 9.0.15-alpha.1 → 9.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/constants/unified.js +0 -29
- package/global-styles.js +1 -1
- package/package.json +1 -1
- package/services/tests/api.test.js +20 -35
- package/utils/commonUtils.js +1 -19
- package/v2Components/CapActionButton/constants.js +0 -7
- package/v2Components/CapActionButton/index.js +109 -168
- package/v2Components/CapActionButton/index.scss +5 -156
- package/v2Components/CapActionButton/messages.js +3 -19
- package/v2Components/CapActionButton/tests/index.test.js +18 -42
- package/v2Components/CapImageUpload/index.js +2 -2
- package/v2Components/CapTagList/index.js +0 -10
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +48 -70
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -214
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +9 -83
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -60
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +5 -10
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +23 -201
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +76 -355
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +8 -155
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +0 -11
- package/v2Components/CommonTestAndPreview/constants.js +2 -38
- package/v2Components/CommonTestAndPreview/index.js +222 -761
- package/v2Components/CommonTestAndPreview/messages.js +3 -45
- package/v2Components/CommonTestAndPreview/sagas.js +6 -25
- package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +0 -1
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +267 -284
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +65 -231
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +5 -118
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -341
- package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +1 -8
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +13 -34
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +283 -281
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
- package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -133
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +24 -31
- package/v2Components/TemplatePreview/_templatePreview.scss +22 -41
- package/v2Components/TemplatePreview/index.js +31 -143
- package/v2Components/TemplatePreview/tests/index.test.js +0 -142
- package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +6 -6
- package/v2Components/TestAndPreviewSlidebox/index.js +1 -13
- package/v2Components/TestAndPreviewSlidebox/sagas.js +4 -11
- package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +1 -3
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +1 -4
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +0 -4
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +20 -20
- package/v2Containers/CreativesContainer/SlideBoxContent.js +7 -36
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +5 -14
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +5 -36
- package/v2Containers/CreativesContainer/constants.js +0 -11
- package/v2Containers/CreativesContainer/index.js +104 -323
- package/v2Containers/CreativesContainer/index.scss +1 -83
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -79
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +90 -333
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +15 -20
- package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
- package/v2Containers/Email/index.js +5 -2
- package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
- package/v2Containers/Rcs/constants.js +11 -131
- package/v2Containers/Rcs/index.js +810 -2605
- package/v2Containers/Rcs/index.scss +8 -289
- package/v2Containers/Rcs/messages.js +3 -34
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +38537 -73535
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +8 -0
- package/v2Containers/Rcs/tests/index.test.js +36 -147
- package/v2Containers/Rcs/tests/mockData.js +0 -38
- package/v2Containers/Rcs/tests/utils.test.js +30 -646
- package/v2Containers/Rcs/utils.js +11 -478
- package/v2Containers/Sms/Create/index.js +48 -115
- package/v2Containers/SmsTrai/Create/index.js +4 -9
- package/v2Containers/SmsTrai/Edit/constants.js +0 -2
- package/v2Containers/SmsTrai/Edit/index.js +169 -678
- package/v2Containers/SmsTrai/Edit/messages.js +4 -14
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2854 -5455
- package/v2Containers/SmsWrapper/index.js +8 -37
- package/v2Containers/TagList/index.js +0 -6
- package/v2Containers/Templates/_templates.scss +12 -171
- package/v2Containers/Templates/actions.js +0 -11
- package/v2Containers/Templates/constants.js +0 -2
- package/v2Containers/Templates/index.js +55 -125
- package/v2Containers/Templates/sagas.js +13 -57
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1015 -1060
- package/v2Containers/Templates/tests/sagas.test.js +16 -199
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -72
- package/v2Containers/TemplatesV2/index.js +23 -86
- package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
- package/v2Containers/Whatsapp/index.js +20 -3
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -578
- package/utils/rcsPayloadUtils.js +0 -92
- package/utils/templateVarUtils.js +0 -201
- package/utils/tests/rcsPayloadUtils.test.js +0 -226
- package/utils/tests/templateVarUtils.test.js +0 -204
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
- package/v2Components/CommonTestAndPreview/utils.js +0 -84
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -91
- package/v2Components/SmsFallback/constants.js +0 -94
- package/v2Components/SmsFallback/index.js +0 -958
- package/v2Components/SmsFallback/index.scss +0 -266
- package/v2Components/SmsFallback/messages.js +0 -78
- package/v2Components/SmsFallback/smsFallbackUtils.js +0 -120
- package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
- package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
- package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
- package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -208
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -309
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
- package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
- package/v2Components/TemplatePreview/constants.js +0 -2
- package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
- package/v2Components/VarSegmentMessageEditor/index.js +0 -125
- package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
- package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -17
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -79
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
- package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
- package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -227
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -318
- package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
- package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
- package/v2Containers/SmsTrai/Edit/index.scss +0 -126
- package/v2Containers/Templates/TemplatesActionBar.js +0 -101
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
- package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
- package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +0 -131
|
@@ -31,13 +31,7 @@ import CustomValuesEditor from './CustomValuesEditor';
|
|
|
31
31
|
import SendTestMessage from './SendTestMessage';
|
|
32
32
|
import PreviewSection from './PreviewSection';
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
import {
|
|
36
|
-
smsTemplateHasMustacheTags,
|
|
37
|
-
buildSyntheticSmsMustacheTags,
|
|
38
|
-
normalizeRcsTestCardMedia,
|
|
39
|
-
mapRcsSuggestionForTestMeta,
|
|
40
|
-
} from './utils';
|
|
34
|
+
// Import constants
|
|
41
35
|
import AddTestCustomerButton from './AddTestCustomer';
|
|
42
36
|
import ExistingCustomerModal from './ExistingCustomerModal';
|
|
43
37
|
// Import constants
|
|
@@ -87,23 +81,12 @@ import {
|
|
|
87
81
|
IMAGE,
|
|
88
82
|
VIDEO,
|
|
89
83
|
URL,
|
|
90
|
-
PREVIEW_TAB_RCS,
|
|
91
|
-
PREVIEW_TAB_SMS_FALLBACK,
|
|
92
84
|
CHANNELS_USING_ANDROID_PREVIEW_DEVICE,
|
|
93
85
|
DAYS,
|
|
94
|
-
RCS_TEST_META_CONTENT_TYPE_RICHCARD,
|
|
95
|
-
RCS_TEST_META_CARD_TYPE_STANDALONE,
|
|
96
|
-
RCS_TEST_META_CARD_ORIENTATION_VERTICAL,
|
|
97
|
-
RCS_TEST_META_CARD_WIDTH_SMALL,
|
|
98
86
|
} from './constants';
|
|
99
|
-
import { getCdnUrl } from '../../utils/cdnTransformation';
|
|
100
|
-
import {
|
|
101
|
-
normalizePreviewApiPayload,
|
|
102
|
-
extractPreviewFromLiquidResponse,
|
|
103
|
-
getSmsFallbackTextForTagExtraction,
|
|
104
|
-
} from './previewApiUtils';
|
|
105
|
-
import { pickFirstSmsFallbackTemplateString } from '../../v2Containers/Rcs/rcsLibraryHydrationUtils';
|
|
106
87
|
|
|
88
|
+
// Import utilities
|
|
89
|
+
import { getCdnUrl } from '../../utils/cdnTransformation';
|
|
107
90
|
import { isValidEmail, isValidMobile, formatPhoneNumber } from '../../utils/commonUtils';
|
|
108
91
|
import { getMembersLookup } from '../../services/api';
|
|
109
92
|
|
|
@@ -126,32 +109,6 @@ const filterUsableGsmSendersForDomain = (domain, gsmSenders, { skipDomainNameEch
|
|
|
126
109
|
});
|
|
127
110
|
};
|
|
128
111
|
|
|
129
|
-
/** Preview payload from Redux may be an Immutable Map — normalize for React state. */
|
|
130
|
-
const toPlainPreviewData = (data) => {
|
|
131
|
-
if (data == null) return null;
|
|
132
|
-
const plain = typeof data.toJS === 'function' ? data.toJS() : data;
|
|
133
|
-
return normalizePreviewApiPayload(plain);
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Merge existing customValues with tag keys from categorized groups.
|
|
138
|
-
* Each group is { required, optional } (arrays of tag objects with fullPath).
|
|
139
|
-
* Preserves existing values; adds '' for any tag key not yet present.
|
|
140
|
-
* Reusable for RCS+fallback and any flow that needs to ensure customValues has keys for tags.
|
|
141
|
-
*/
|
|
142
|
-
const mergeCustomValuesWithTagKeys = (prev, ...categorizedGroups) => {
|
|
143
|
-
const next = { ...(prev || {}) };
|
|
144
|
-
categorizedGroups.forEach((group) => {
|
|
145
|
-
[...(group.required || []), ...(group.optional || [])].forEach((tag) => {
|
|
146
|
-
const key = tag?.fullPath;
|
|
147
|
-
if (key && next[key] === undefined) next[key] = '';
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
return next;
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
/** True when `body` contains `{{name}}` mustache tokens (user-fillable personalization tags).
|
|
154
|
-
* DLT `{#name#}` slots are pre-bound template variables and are intentionally excluded. */
|
|
155
112
|
|
|
156
113
|
/**
|
|
157
114
|
* CapTreeSelect and group resolution use strict equality; API data may mix numeric and string ids.
|
|
@@ -177,7 +134,7 @@ const testEntityIdsEqual = (a, b) => {
|
|
|
177
134
|
*/
|
|
178
135
|
const CommonTestAndPreview = (props) => {
|
|
179
136
|
const {
|
|
180
|
-
intl: { formatMessage
|
|
137
|
+
intl: { formatMessage },
|
|
181
138
|
show,
|
|
182
139
|
onClose,
|
|
183
140
|
channel, // The channel: 'EMAIL', 'SMS', 'RCS', etc.
|
|
@@ -213,23 +170,13 @@ const CommonTestAndPreview = (props) => {
|
|
|
213
170
|
...additionalProps
|
|
214
171
|
} = props;
|
|
215
172
|
|
|
216
|
-
const smsFallbackContent = additionalProps?.smsFallbackContent;
|
|
217
|
-
const smsFallbackTextForTagExtraction = useMemo(
|
|
218
|
-
() => getSmsFallbackTextForTagExtraction(smsFallbackContent),
|
|
219
|
-
[smsFallbackContent],
|
|
220
|
-
);
|
|
221
173
|
// ============================================
|
|
222
174
|
// STATE MANAGEMENT
|
|
223
175
|
// ============================================
|
|
224
176
|
const [selectedCustomer, setSelectedCustomer] = useState(null);
|
|
225
177
|
const [requiredTags, setRequiredTags] = useState([]);
|
|
226
178
|
const [optionalTags, setOptionalTags] = useState([]);
|
|
227
|
-
const [smsFallbackExtractedTags, setSmsFallbackExtractedTags] = useState([]);
|
|
228
|
-
const [smsFallbackRequiredTags, setSmsFallbackRequiredTags] = useState([]);
|
|
229
|
-
const [smsFallbackOptionalTags, setSmsFallbackOptionalTags] = useState([]);
|
|
230
|
-
const [isExtractingSmsFallbackTags, setIsExtractingSmsFallbackTags] = useState(false);
|
|
231
179
|
const [customValues, setCustomValues] = useState({});
|
|
232
|
-
const previewCustomValuesRef = useRef({});
|
|
233
180
|
const [showJSON, setShowJSON] = useState(false);
|
|
234
181
|
const [tagsExtracted, setTagsExtracted] = useState(false);
|
|
235
182
|
|
|
@@ -240,8 +187,6 @@ const CommonTestAndPreview = (props) => {
|
|
|
240
187
|
const [customerData, setCustomerData] = useState({ name: '', email: '', mobile: '', customerId: '' });
|
|
241
188
|
|
|
242
189
|
const [previewDevice, setPreviewDevice] = useState(initialDevice);
|
|
243
|
-
const [activePreviewTab, setActivePreviewTab] = useState(PREVIEW_TAB_RCS);
|
|
244
|
-
const [smsFallbackPreviewText, setSmsFallbackPreviewText] = useState(undefined);
|
|
245
190
|
// Track if a preview call has been made (to know when to use previewDataHtml vs raw content)
|
|
246
191
|
const [hasPreviewCallBeenMade, setHasPreviewCallBeenMade] = useState(false);
|
|
247
192
|
const [previewDataHtml, setPreviewDataHtml] = useState(() => {
|
|
@@ -274,22 +219,15 @@ const CommonTestAndPreview = (props) => {
|
|
|
274
219
|
[CHANNELS.WHATSAPP]: {
|
|
275
220
|
domainId: null, senderMobNum: '', sourceAccountIdentifier: '',
|
|
276
221
|
},
|
|
277
|
-
[CHANNELS.RCS]: {
|
|
278
|
-
domainId: null,
|
|
279
|
-
domainGatewayMapId: null,
|
|
280
|
-
gsmSenderId: '',
|
|
281
|
-
smsFallbackDomainId: null,
|
|
282
|
-
cdmaSenderId: '', // gsmSenderId = RCS sender (domainId|senderId), cdmaSenderId = SMS fallback
|
|
283
|
-
},
|
|
284
222
|
});
|
|
285
223
|
|
|
286
|
-
const channelsWithDeliverySettings = [CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP
|
|
224
|
+
const channelsWithDeliverySettings = [CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP];
|
|
287
225
|
const formDataForSendTest = formData ?? (content && typeof content === 'object' && !Array.isArray(content) ? content : formData);
|
|
288
226
|
const smsTemplateConfigs = formDataForSendTest?.templateConfigs || {};
|
|
289
227
|
const smsTraiDltEnabled = !!smsTemplateConfigs?.traiDltEnabled;
|
|
290
228
|
const registeredSenderIds = smsTemplateConfigs?.registeredSenderIds || [];
|
|
291
229
|
|
|
292
|
-
// Fetch sender details and WeCRM accounts when Test & Preview opens
|
|
230
|
+
// Fetch sender details and WeCRM accounts when Test & Preview opens for SMS/Email/WhatsApp
|
|
293
231
|
useEffect(() => {
|
|
294
232
|
if (!show || !channel) {
|
|
295
233
|
return;
|
|
@@ -298,99 +236,39 @@ const CommonTestAndPreview = (props) => {
|
|
|
298
236
|
if (actions.getSenderDetailsRequested) {
|
|
299
237
|
actions.getSenderDetailsRequested({ channel, orgUnitId: orgUnitId ?? -1 });
|
|
300
238
|
}
|
|
301
|
-
// SMS domains/senders are needed for RCS delivery UI (fallback row + slidebox) whenever RCS is open — not only when fallback body exists.
|
|
302
|
-
if (channel === CHANNELS.RCS && actions.getSenderDetailsRequested) {
|
|
303
|
-
actions.getSenderDetailsRequested({ channel: CHANNELS.SMS, orgUnitId: orgUnitId ?? -1 });
|
|
304
|
-
}
|
|
305
239
|
if (channel === CHANNELS.WHATSAPP && actions.getWeCrmAccountsRequested) {
|
|
306
240
|
actions.getWeCrmAccountsRequested({ sourceName: CHANNELS.WHATSAPP });
|
|
307
241
|
}
|
|
308
242
|
}
|
|
309
243
|
}, [show, channel, orgUnitId, actions]);
|
|
310
244
|
|
|
245
|
+
useEffect(() => {
|
|
246
|
+
if (!show) {
|
|
247
|
+
setCustomerModal([false, '']);
|
|
248
|
+
setSearchValue('');
|
|
249
|
+
setCustomerData({ name: '', email: '', mobile: '', customerId: '' });
|
|
250
|
+
}
|
|
251
|
+
}, [show]);
|
|
252
|
+
|
|
311
253
|
const findDefault = (arr) => (arr && arr.find((x) => x.default)) || (arr && arr[0]) || {};
|
|
312
254
|
|
|
313
255
|
// Auto-set default delivery setting when sender details load (campaigns-style: first domain + default/first sender)
|
|
314
256
|
useEffect(() => {
|
|
315
257
|
if (!channel || !channelsWithDeliverySettings.includes(channel)) return;
|
|
316
|
-
|
|
317
|
-
if (channel === CHANNELS.RCS) {
|
|
318
|
-
const rcsDomainRows = senderDetailsByChannel?.[CHANNELS.RCS] || [];
|
|
319
|
-
const smsFallbackDomainRows = senderDetailsByChannel?.[CHANNELS.SMS] || [];
|
|
320
|
-
if (!rcsDomainRows.length) return;
|
|
321
|
-
|
|
322
|
-
const currentRcsDeliverySettings = testPreviewDeliverySettings?.[CHANNELS.RCS] || {};
|
|
323
|
-
const isRcsGsmSenderUnset = !currentRcsDeliverySettings?.gsmSenderId;
|
|
324
|
-
const isSmsFallbackSenderUnset = !currentRcsDeliverySettings?.cdmaSenderId;
|
|
325
|
-
const isRcsDeliveryFullyUnset = isRcsGsmSenderUnset && isSmsFallbackSenderUnset;
|
|
326
|
-
const shouldOnlyFillSmsFallbackSender =
|
|
327
|
-
!isRcsGsmSenderUnset && isSmsFallbackSenderUnset && smsFallbackDomainRows.length > 0;
|
|
328
|
-
|
|
329
|
-
if (!isRcsDeliveryFullyUnset && !shouldOnlyFillSmsFallbackSender) return;
|
|
330
|
-
|
|
331
|
-
const firstRcsDomain = rcsDomainRows[0];
|
|
332
|
-
const firstSmsFallbackDomain = smsFallbackDomainRows[0];
|
|
333
|
-
const usableRcsGsmSenders = filterUsableGsmSendersForDomain(
|
|
334
|
-
firstRcsDomain,
|
|
335
|
-
firstRcsDomain?.gsmSenders,
|
|
336
|
-
{ skipDomainNameEchoFilter: true },
|
|
337
|
-
);
|
|
338
|
-
const usableSmsFallbackGsmSenders = firstSmsFallbackDomain
|
|
339
|
-
? filterUsableGsmSendersForDomain(firstSmsFallbackDomain, firstSmsFallbackDomain?.gsmSenders)
|
|
340
|
-
: [];
|
|
341
|
-
const defaultRcsGsmSender = usableRcsGsmSenders[0];
|
|
342
|
-
const defaultSmsFallbackGsmSender = usableSmsFallbackGsmSenders[0];
|
|
343
|
-
const rcsSenderCompositeValue =
|
|
344
|
-
firstRcsDomain?.domainId != null && defaultRcsGsmSender?.value != null
|
|
345
|
-
? `${firstRcsDomain.domainId}|${defaultRcsGsmSender.value}`
|
|
346
|
-
: (defaultRcsGsmSender?.value || '');
|
|
347
|
-
const smsFallbackSenderCompositeValue =
|
|
348
|
-
firstSmsFallbackDomain?.domainId != null && defaultSmsFallbackGsmSender?.value != null
|
|
349
|
-
? `${firstSmsFallbackDomain.domainId}|${defaultSmsFallbackGsmSender.value}`
|
|
350
|
-
: (defaultSmsFallbackGsmSender?.value || '');
|
|
351
|
-
|
|
352
|
-
setTestPreviewDeliverySettings((prev) => {
|
|
353
|
-
const previousRcsSettings = prev?.[CHANNELS.RCS] || {};
|
|
354
|
-
if (shouldOnlyFillSmsFallbackSender) {
|
|
355
|
-
if (!smsFallbackSenderCompositeValue) return prev;
|
|
356
|
-
return {
|
|
357
|
-
...prev,
|
|
358
|
-
[CHANNELS.RCS]: {
|
|
359
|
-
...previousRcsSettings,
|
|
360
|
-
smsFallbackDomainId: firstSmsFallbackDomain?.domainId ?? null,
|
|
361
|
-
cdmaSenderId: smsFallbackSenderCompositeValue,
|
|
362
|
-
},
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
return {
|
|
366
|
-
...prev,
|
|
367
|
-
[CHANNELS.RCS]: {
|
|
368
|
-
domainId: firstRcsDomain?.domainId ?? null,
|
|
369
|
-
domainGatewayMapId: firstRcsDomain?.dgmId ?? null,
|
|
370
|
-
gsmSenderId: rcsSenderCompositeValue,
|
|
371
|
-
smsFallbackDomainId: firstSmsFallbackDomain?.domainId ?? null,
|
|
372
|
-
cdmaSenderId: smsFallbackSenderCompositeValue,
|
|
373
|
-
},
|
|
374
|
-
};
|
|
375
|
-
});
|
|
376
|
-
return;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
258
|
const domains = senderDetailsByChannel[channel];
|
|
380
259
|
if (!domains || domains.length === 0) return;
|
|
381
260
|
const {
|
|
382
|
-
domainId = '', gsmSenderId = '',
|
|
261
|
+
domainId = '', gsmSenderId = '', senderEmail = '', senderMobNum = '',
|
|
383
262
|
} = testPreviewDeliverySettings[channel] || {};
|
|
384
|
-
const isEmptySelection = !domainId && !gsmSenderId && !
|
|
263
|
+
const isEmptySelection = !domainId && !gsmSenderId && !senderEmail && !senderMobNum;
|
|
385
264
|
if (!isEmptySelection) return;
|
|
386
265
|
|
|
387
266
|
const whatsappAccountFromForm = channel === CHANNELS.WHATSAPP ? formData?.accountName : undefined;
|
|
388
267
|
const matchedWhatsappAccount = whatsappAccountFromForm
|
|
389
268
|
? (wecrmAccounts || []).find((account) => account?.name === whatsappAccountFromForm)
|
|
390
269
|
: null;
|
|
391
|
-
const smsDomains = channel === CHANNELS.SMS && smsTraiDltEnabled
|
|
392
|
-
? domains.filter((domain) => (domain
|
|
393
|
-
registeredSenderIds?.includes(gsm?.value)))
|
|
270
|
+
const smsDomains = channel === CHANNELS.SMS && smsTraiDltEnabled
|
|
271
|
+
? domains.filter((domain) => (domain.gsmSenders || []).some((gsm) => registeredSenderIds.includes(gsm.value)))
|
|
394
272
|
: domains;
|
|
395
273
|
const [defaultDomain] = domains;
|
|
396
274
|
const [firstSmsDomain] = smsDomains;
|
|
@@ -405,8 +283,8 @@ const CommonTestAndPreview = (props) => {
|
|
|
405
283
|
const next = { ...prev };
|
|
406
284
|
if (channel === CHANNELS.SMS) {
|
|
407
285
|
const smsGsmSenders = smsTraiDltEnabled
|
|
408
|
-
? (firstDomain
|
|
409
|
-
: firstDomain
|
|
286
|
+
? (firstDomain.gsmSenders || []).filter((gsm) => registeredSenderIds.includes(gsm.value))
|
|
287
|
+
: firstDomain.gsmSenders;
|
|
410
288
|
next[channel] = {
|
|
411
289
|
domainId: firstDomain.domainId,
|
|
412
290
|
domainGatewayMapId: firstDomain.dgmId,
|
|
@@ -439,29 +317,10 @@ const CommonTestAndPreview = (props) => {
|
|
|
439
317
|
// MEMOIZED VALUES
|
|
440
318
|
// ============================================
|
|
441
319
|
|
|
442
|
-
const allTags = useMemo(
|
|
443
|
-
() => [...requiredTags, ...optionalTags, ...smsFallbackRequiredTags, ...smsFallbackOptionalTags],
|
|
444
|
-
[requiredTags, optionalTags, smsFallbackRequiredTags, smsFallbackOptionalTags]
|
|
445
|
-
);
|
|
446
|
-
|
|
447
|
-
const allRequiredTags = useMemo(
|
|
448
|
-
() => [...requiredTags, ...smsFallbackRequiredTags],
|
|
449
|
-
[requiredTags, smsFallbackRequiredTags]
|
|
450
|
-
);
|
|
451
|
-
|
|
452
|
-
const buildEmptyValues = useCallback(
|
|
453
|
-
() => allTags.reduce((acc, tag) => {
|
|
454
|
-
const key = tag?.fullPath;
|
|
455
|
-
if (key) acc[key] = '';
|
|
456
|
-
return acc;
|
|
457
|
-
}, {}),
|
|
458
|
-
[allTags]
|
|
459
|
-
);
|
|
460
|
-
|
|
461
320
|
// Check if update preview button should be disabled
|
|
462
321
|
const isUpdatePreviewDisabled = useMemo(() => (
|
|
463
|
-
|
|
464
|
-
), [
|
|
322
|
+
requiredTags.some((tag) => !customValues[tag.fullPath])
|
|
323
|
+
), [requiredTags, customValues]);
|
|
465
324
|
|
|
466
325
|
// Get current content based on channel and editor type
|
|
467
326
|
const getCurrentContent = useMemo(() => {
|
|
@@ -505,13 +364,6 @@ const CommonTestAndPreview = (props) => {
|
|
|
505
364
|
return currentTabData.base['sms-editor'];
|
|
506
365
|
}
|
|
507
366
|
}
|
|
508
|
-
// DLT / Test & Preview shape: { templateConfigs: { template, templateId, ... } }
|
|
509
|
-
if (formData.templateConfigs?.template) {
|
|
510
|
-
const smsDltTemplateValue = formData.templateConfigs.template;
|
|
511
|
-
if (typeof smsDltTemplateValue === 'string') return smsDltTemplateValue;
|
|
512
|
-
if (Array.isArray(smsDltTemplateValue)) return smsDltTemplateValue.join('');
|
|
513
|
-
return '';
|
|
514
|
-
}
|
|
515
367
|
}
|
|
516
368
|
|
|
517
369
|
// SMS channel fallback - if formData is not available, use content directly
|
|
@@ -564,70 +416,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
564
416
|
return content || '';
|
|
565
417
|
}, [channel, formData, currentTab, beeContent, content, beeInstance]);
|
|
566
418
|
|
|
567
|
-
|
|
568
|
-
if (channel === CHANNELS.SMS) {
|
|
569
|
-
const smsEditorBody = typeof getCurrentContent === 'string' ? getCurrentContent : '';
|
|
570
|
-
if (!smsTemplateHasMustacheTags(smsEditorBody)) return [];
|
|
571
|
-
const extractTagsFromApi = extractedTags ?? [];
|
|
572
|
-
if (extractTagsFromApi.length > 0) return extractTagsFromApi;
|
|
573
|
-
return buildSyntheticSmsMustacheTags(smsEditorBody);
|
|
574
|
-
}
|
|
575
|
-
const hasFallbackSmsBody = !!(smsFallbackContent?.templateContent || smsFallbackContent?.content);
|
|
576
|
-
if (channel === CHANNELS.RCS && hasFallbackSmsBody) {
|
|
577
|
-
const rcsPrimaryTags = extractedTags ?? [];
|
|
578
|
-
const fallbackSmsTextForTags = smsFallbackTextForTagExtraction ?? '';
|
|
579
|
-
const fallbackSmsTagRows = smsTemplateHasMustacheTags(fallbackSmsTextForTags)
|
|
580
|
-
? (smsFallbackExtractedTags?.length > 0
|
|
581
|
-
? smsFallbackExtractedTags
|
|
582
|
-
: buildSyntheticSmsMustacheTags(fallbackSmsTextForTags))
|
|
583
|
-
: [];
|
|
584
|
-
const mergedRcsAndFallbackTags = [...rcsPrimaryTags, ...fallbackSmsTagRows];
|
|
585
|
-
if (mergedRcsAndFallbackTags.length > 0) return mergedRcsAndFallbackTags;
|
|
586
|
-
return buildSyntheticSmsMustacheTags(fallbackSmsTextForTags);
|
|
587
|
-
}
|
|
588
|
-
return extractedTags ?? [];
|
|
589
|
-
}, [
|
|
590
|
-
channel,
|
|
591
|
-
extractedTags,
|
|
592
|
-
getCurrentContent,
|
|
593
|
-
smsFallbackContent,
|
|
594
|
-
smsFallbackExtractedTags,
|
|
595
|
-
smsFallbackTextForTagExtraction,
|
|
596
|
-
]);
|
|
597
|
-
|
|
598
|
-
const isRcsSmsFallbackPreviewEnabled =
|
|
599
|
-
channel === CHANNELS.RCS
|
|
600
|
-
&& !!(smsFallbackContent?.templateContent || smsFallbackContent?.content);
|
|
601
|
-
// Only treat as SMS when user is on the Fallback SMS tab — not whenever fallback exists (RCS tab needs RCS preview API).
|
|
602
|
-
const isSmsFallbackTabActive = isRcsSmsFallbackPreviewEnabled && activePreviewTab === PREVIEW_TAB_SMS_FALLBACK;
|
|
603
|
-
const activeChannelForActions = isSmsFallbackTabActive ? CHANNELS.SMS : channel;
|
|
604
|
-
// VarSegment slot values live in rcsSmsFallbackVarMapped; raw templateContent alone is stale for /preview Body.
|
|
605
|
-
const resolvedSmsFallbackBodyForPreviewTab =
|
|
606
|
-
smsFallbackTextForTagExtraction
|
|
607
|
-
|| smsFallbackContent?.templateContent
|
|
608
|
-
|| smsFallbackContent?.content
|
|
609
|
-
|| '';
|
|
610
|
-
const activeContentForActions = isSmsFallbackTabActive
|
|
611
|
-
? resolvedSmsFallbackBodyForPreviewTab
|
|
612
|
-
: getCurrentContent;
|
|
613
|
-
|
|
614
|
-
/**
|
|
615
|
-
* SMS fallback pane must show /preview API result when user updated preview on that tab (plain text).
|
|
616
|
-
* Skip when resolvedBody is RCS-shaped (e.g. user last previewed on RCS tab).
|
|
617
|
-
*/
|
|
618
|
-
// smsFallbackPreviewText is the single source of truth for the resolved SMS fallback preview.
|
|
619
|
-
// It is set only by syncSmsFallbackPreview (called from handleUpdatePreview and the
|
|
620
|
-
// prefilled-values effect) and reset to undefined on discard / slidebox close.
|
|
621
|
-
// Using previewDataHtml as a fallback is unsafe because that state is shared with the primary
|
|
622
|
-
// RCS preview and can contain stale SMS or RCS content.
|
|
623
|
-
const smsFallbackResolvedText = useMemo(() => {
|
|
624
|
-
const hasFallbackBody = !!(smsFallbackContent?.templateContent || smsFallbackContent?.content);
|
|
625
|
-
if (channel !== CHANNELS.RCS || !hasFallbackBody) return undefined;
|
|
626
|
-
if (smsFallbackPreviewText != null) return smsFallbackPreviewText;
|
|
627
|
-
return undefined;
|
|
628
|
-
}, [channel, smsFallbackContent, smsFallbackPreviewText]);
|
|
629
|
-
|
|
630
|
-
// Build test entities tree data from testCustomers prop
|
|
419
|
+
// Build test entities tree data
|
|
631
420
|
// Build test entities tree data from testCustomers prop (includes customers added via addTestCustomer action)
|
|
632
421
|
const testEntitiesTreeData = useMemo(() => {
|
|
633
422
|
const groupsNode = {
|
|
@@ -636,7 +425,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
636
425
|
selectable: false,
|
|
637
426
|
children: testGroups?.map((group) => ({
|
|
638
427
|
title: group?.groupName,
|
|
639
|
-
value: normalizeTestEntityId(group?.groupId),
|
|
428
|
+
value: 'group:' + normalizeTestEntityId(group?.groupId),
|
|
640
429
|
})),
|
|
641
430
|
};
|
|
642
431
|
|
|
@@ -646,7 +435,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
646
435
|
selectable: false,
|
|
647
436
|
children: testCustomers?.map((customer) => ({
|
|
648
437
|
title: customer?.name?.trim() || customer?.email?.trim() || customer?.mobile?.trim() || customer?.userId || customer?.customerId,
|
|
649
|
-
value: normalizeTestEntityId(customer?.userId ?? customer?.customerId),
|
|
438
|
+
value: 'customer:' + normalizeTestEntityId(customer?.userId ?? customer?.customerId),
|
|
650
439
|
})) || [],
|
|
651
440
|
};
|
|
652
441
|
|
|
@@ -726,10 +515,11 @@ const CommonTestAndPreview = (props) => {
|
|
|
726
515
|
email: customerData?.email || '',
|
|
727
516
|
mobile: customerData?.mobile || '',
|
|
728
517
|
});
|
|
518
|
+
const prefixedAddedId = 'customer:' + normalizedAddedId;
|
|
729
519
|
setSelectedTestEntities((prev) => (
|
|
730
|
-
prev.some((id) =>
|
|
520
|
+
prev.some((id) => id === prefixedAddedId)
|
|
731
521
|
? prev
|
|
732
|
-
: [...prev,
|
|
522
|
+
: [...prev, prefixedAddedId]
|
|
733
523
|
));
|
|
734
524
|
}
|
|
735
525
|
handleCloseCustomerModal();
|
|
@@ -793,33 +583,13 @@ const CommonTestAndPreview = (props) => {
|
|
|
793
583
|
whatsappContent: formDataObj?.whatsappContent,
|
|
794
584
|
};
|
|
795
585
|
|
|
796
|
-
case CHANNELS.RCS:
|
|
797
|
-
// For carousel, replace {{N}} tokens in carouselData with resolved semantic vars from
|
|
798
|
-
// formData.cardContent so the preview API can resolve {{member.firstName}} etc.
|
|
799
|
-
const formDataCardContent =
|
|
800
|
-
formDataObj?.versions?.base?.content?.RCS?.rcsContent?.cardContent
|
|
801
|
-
?? formDataObj?.content?.RCS?.rcsContent?.cardContent;
|
|
802
|
-
let parsedRcs = {};
|
|
803
|
-
try { parsedRcs = JSON.parse(contentStr); } catch (e) { parsedRcs = typeof contentStr === 'object' ? contentStr : {}; }
|
|
804
|
-
if (Array.isArray(parsedRcs.carouselData) && Array.isArray(formDataCardContent) && formDataCardContent.length > 0) {
|
|
805
|
-
const resolvedCarouselData = parsedRcs.carouselData.map((card, idx) => {
|
|
806
|
-
const formCard = formDataCardContent[idx] || {};
|
|
807
|
-
return {
|
|
808
|
-
...card,
|
|
809
|
-
title: formCard.title || card.title,
|
|
810
|
-
bodyText: formCard.description || card.bodyText,
|
|
811
|
-
};
|
|
812
|
-
});
|
|
813
|
-
return {
|
|
814
|
-
...basePayload,
|
|
815
|
-
messageBody: JSON.stringify({ ...parsedRcs, carouselData: resolvedCarouselData }),
|
|
816
|
-
};
|
|
817
|
-
}
|
|
586
|
+
case CHANNELS.RCS:
|
|
818
587
|
return {
|
|
819
588
|
...basePayload,
|
|
820
589
|
messageBody: contentStr,
|
|
590
|
+
// messageTitle: resolveTagsInText(formDataObj?.rcsTitle || '', customValuesObj),
|
|
591
|
+
// rcsDesc: formDataObj?.rcsDesc,
|
|
821
592
|
};
|
|
822
|
-
}
|
|
823
593
|
|
|
824
594
|
case CHANNELS.INAPP:
|
|
825
595
|
return {
|
|
@@ -859,37 +629,6 @@ const CommonTestAndPreview = (props) => {
|
|
|
859
629
|
}
|
|
860
630
|
};
|
|
861
631
|
|
|
862
|
-
/**
|
|
863
|
-
* When RCS has SMS fallback, refresh fallback preview text via the same Liquid /preview API
|
|
864
|
-
* (separate call with SMS channel + fallback template body). Used after primary preview updates.
|
|
865
|
-
*/
|
|
866
|
-
const syncSmsFallbackPreview = async (customValuesForResolve, selectedCustomerObj) => {
|
|
867
|
-
const fallbackBodyForLiquidPreview =
|
|
868
|
-
getSmsFallbackTextForTagExtraction(smsFallbackContent)
|
|
869
|
-
|| smsFallbackContent?.templateContent
|
|
870
|
-
|| smsFallbackContent?.content
|
|
871
|
-
|| '';
|
|
872
|
-
if (channel !== CHANNELS.RCS || !String(fallbackBodyForLiquidPreview).trim()) return;
|
|
873
|
-
try {
|
|
874
|
-
const smsFallbackPayload = preparePreviewPayload(
|
|
875
|
-
CHANNELS.SMS,
|
|
876
|
-
formData || {},
|
|
877
|
-
fallbackBodyForLiquidPreview,
|
|
878
|
-
customValuesForResolve,
|
|
879
|
-
selectedCustomerObj
|
|
880
|
-
);
|
|
881
|
-
const fallbackResponse = await Api.updateEmailPreview(smsFallbackPayload);
|
|
882
|
-
const fallbackPreview = extractPreviewFromLiquidResponse(fallbackResponse);
|
|
883
|
-
setSmsFallbackPreviewText(
|
|
884
|
-
typeof fallbackPreview?.resolvedBody === 'string'
|
|
885
|
-
? fallbackPreview.resolvedBody
|
|
886
|
-
: undefined
|
|
887
|
-
);
|
|
888
|
-
} catch (e) {
|
|
889
|
-
/* keep existing smsFallbackPreviewText on failure */
|
|
890
|
-
}
|
|
891
|
-
};
|
|
892
|
-
|
|
893
632
|
/**
|
|
894
633
|
* Prepare payload for tag extraction based on channel
|
|
895
634
|
*/
|
|
@@ -913,32 +652,11 @@ const CommonTestAndPreview = (props) => {
|
|
|
913
652
|
templateContent: contentStr,
|
|
914
653
|
};
|
|
915
654
|
|
|
916
|
-
case CHANNELS.RCS:
|
|
917
|
-
// Carousel templates don't have rcsTitle/rcsDesc — their content lives in cardContent[].
|
|
918
|
-
// Use the resolved card strings from formDataObj (resolved by resolveTemplateWithMap in
|
|
919
|
-
// testPreviewFormData) so the extraction API receives actual Capillary tag expressions
|
|
920
|
-
// instead of numeric {{1}}/{{2}} placeholders.
|
|
921
|
-
const rcsCardContentArr =
|
|
922
|
-
formDataObj?.versions?.base?.content?.RCS?.rcsContent?.cardContent
|
|
923
|
-
?? formDataObj?.content?.RCS?.rcsContent?.cardContent;
|
|
924
|
-
if (Array.isArray(rcsCardContentArr) && rcsCardContentArr.length > 0) {
|
|
925
|
-
const carouselTagText = rcsCardContentArr
|
|
926
|
-
.map((c) => `${c.title || ''} ${c.description || ''}`)
|
|
927
|
-
.join(' ')
|
|
928
|
-
.trim();
|
|
929
|
-
if (carouselTagText) {
|
|
930
|
-
return {
|
|
931
|
-
templateSubject: formDataObj?.rcsTitle || '',
|
|
932
|
-
templateContent: carouselTagText,
|
|
933
|
-
};
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
// Standalone (non-carousel) card: use rcsTitle/rcsDesc directly.
|
|
655
|
+
case CHANNELS.RCS:
|
|
937
656
|
return {
|
|
938
657
|
templateSubject: formDataObj?.rcsTitle || '',
|
|
939
658
|
templateContent: formDataObj?.rcsDesc || contentStr,
|
|
940
659
|
};
|
|
941
|
-
}
|
|
942
660
|
|
|
943
661
|
case CHANNELS.INAPP:
|
|
944
662
|
return {
|
|
@@ -1011,7 +729,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
1011
729
|
} = carousel || {};
|
|
1012
730
|
const buttonData = buttons.map((button, index) => {
|
|
1013
731
|
const {
|
|
1014
|
-
type, text, phone_number
|
|
732
|
+
type, text, phone_number, urlType, url,
|
|
1015
733
|
} = button || {};
|
|
1016
734
|
const buttonObj = {
|
|
1017
735
|
type,
|
|
@@ -1019,7 +737,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
1019
737
|
index,
|
|
1020
738
|
};
|
|
1021
739
|
if (type === PHONE_NUMBER) {
|
|
1022
|
-
buttonObj.phoneNumber =
|
|
740
|
+
buttonObj.phoneNumber = phone_number;
|
|
1023
741
|
}
|
|
1024
742
|
if (type === URL) {
|
|
1025
743
|
buttonObj.url = url;
|
|
@@ -1048,159 +766,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
1048
766
|
};
|
|
1049
767
|
});
|
|
1050
768
|
|
|
1051
|
-
|
|
1052
|
-
* Build createMessageMeta payload for RCS (test message).
|
|
1053
|
-
* rcsMessageContent: { channel, accountId?, rcsRichCardContent: { contentType, cardType, cardSettings, cardContent }, smsFallBackContent? }
|
|
1054
|
-
* Then rcsDeliverySettings, executionParams, clientName last.
|
|
1055
|
-
*/
|
|
1056
|
-
const buildRcsTestMessagePayload = (
|
|
1057
|
-
creativeFormData,
|
|
1058
|
-
_unusedEditorContentString,
|
|
1059
|
-
_customValuesObj,
|
|
1060
|
-
deliverySettingsOverride,
|
|
1061
|
-
basePayload,
|
|
1062
|
-
_rcsTestMetaExtras = {},
|
|
1063
|
-
) => {
|
|
1064
|
-
const rcsSectionFromForm =
|
|
1065
|
-
creativeFormData?.versions?.base?.content?.RCS ?? creativeFormData?.content?.RCS ?? {};
|
|
1066
|
-
const rcsContentFromForm = rcsSectionFromForm?.rcsContent || {};
|
|
1067
|
-
const smsFallbackFromCreativeForm = rcsSectionFromForm?.smsFallBackContent || {};
|
|
1068
|
-
let rcsCardPayloadList = [];
|
|
1069
|
-
if (Array.isArray(rcsContentFromForm?.cardContent)) {
|
|
1070
|
-
rcsCardPayloadList = rcsContentFromForm.cardContent;
|
|
1071
|
-
} else if (rcsContentFromForm?.cardContent) {
|
|
1072
|
-
rcsCardPayloadList = [rcsContentFromForm.cardContent];
|
|
1073
|
-
}
|
|
1074
|
-
// Raw title/description with template tags; SMS fallback uses tagged template fields (pickFirst…).
|
|
1075
|
-
const cardContentForTestMetaApi = rcsCardPayloadList.map((singleRcsCardPayload) => {
|
|
1076
|
-
const normalizedCardMediaForTestApi = singleRcsCardPayload?.media
|
|
1077
|
-
? normalizeRcsTestCardMedia(singleRcsCardPayload.media)
|
|
1078
|
-
: undefined;
|
|
1079
|
-
const suggestionsFromCard = Array.isArray(singleRcsCardPayload?.suggestions)
|
|
1080
|
-
? singleRcsCardPayload.suggestions
|
|
1081
|
-
: [];
|
|
1082
|
-
const suggestionsFormattedForTestMeta = suggestionsFromCard.map((suggestionItem, index) =>
|
|
1083
|
-
mapRcsSuggestionForTestMeta(suggestionItem, index));
|
|
1084
|
-
return {
|
|
1085
|
-
title: singleRcsCardPayload?.title ?? '',
|
|
1086
|
-
description: singleRcsCardPayload?.description ?? '',
|
|
1087
|
-
mediaType: singleRcsCardPayload?.mediaType ?? MEDIA_TYPE_TEXT,
|
|
1088
|
-
...(normalizedCardMediaForTestApi && { media: normalizedCardMediaForTestApi }),
|
|
1089
|
-
...(suggestionsFormattedForTestMeta.length > 0 && {
|
|
1090
|
-
suggestions: suggestionsFormattedForTestMeta,
|
|
1091
|
-
}),
|
|
1092
|
-
};
|
|
1093
|
-
});
|
|
1094
|
-
// Use the component-level smsFallbackContent prop (has rcsSmsFallbackVarMapped) so DLT
|
|
1095
|
-
// {#var#} slots are converted to {{tagName}} mustache tags before sending to createMessageMeta.
|
|
1096
|
-
const smsFallbackTaggedTemplateBody =
|
|
1097
|
-
getSmsFallbackTextForTagExtraction(smsFallbackContent)
|
|
1098
|
-
|| pickFirstSmsFallbackTemplateString(smsFallbackFromCreativeForm)
|
|
1099
|
-
|| '';
|
|
1100
|
-
const smsSenderFromDelivery = deliverySettingsOverride?.cdmaSenderId?.includes('|')
|
|
1101
|
-
? deliverySettingsOverride.cdmaSenderId.split('|')[1]
|
|
1102
|
-
: deliverySettingsOverride?.cdmaSenderId;
|
|
1103
|
-
const deliveryFallbackSmsId =
|
|
1104
|
-
typeof smsSenderFromDelivery === 'string' ? smsSenderFromDelivery.trim() : '';
|
|
1105
|
-
const creativeFallbackSmsId =
|
|
1106
|
-
smsFallbackFromCreativeForm?.senderId != null
|
|
1107
|
-
? String(smsFallbackFromCreativeForm.senderId).trim()
|
|
1108
|
-
: '';
|
|
1109
|
-
const fallbackSmsSenderIdForChannel = deliveryFallbackSmsId || creativeFallbackSmsId || '';
|
|
1110
|
-
|
|
1111
|
-
// For DLT orgs: include templateConfigs inside smsFallBackContent so createMessageMeta
|
|
1112
|
-
// receives the DLT template ID, registered sender IDs, and body alongside the message.
|
|
1113
|
-
// Priority: smsFallBackContent.templateConfigs from the RCS payload (clean API shape,
|
|
1114
|
-
// built by createPayload) → fallback to root-level formData.templateConfigs (strip the
|
|
1115
|
-
// UI-only traiDltEnabled flag before sending to the API).
|
|
1116
|
-
const smsFallbackTemplateConfigsForApi = (() => {
|
|
1117
|
-
if (
|
|
1118
|
-
smsFallbackFromCreativeForm?.templateConfigs
|
|
1119
|
-
&& typeof smsFallbackFromCreativeForm.templateConfigs === 'object'
|
|
1120
|
-
) {
|
|
1121
|
-
return smsFallbackFromCreativeForm.templateConfigs;
|
|
1122
|
-
}
|
|
1123
|
-
const rootTc = creativeFormData?.templateConfigs;
|
|
1124
|
-
if (rootTc && typeof rootTc === 'object') {
|
|
1125
|
-
// eslint-disable-next-line no-unused-vars
|
|
1126
|
-
const { traiDltEnabled: _uiFlag, ...tcForApi } = rootTc;
|
|
1127
|
-
return Object.keys(tcForApi).length > 0 ? tcForApi : null;
|
|
1128
|
-
}
|
|
1129
|
-
return null;
|
|
1130
|
-
})();
|
|
1131
|
-
|
|
1132
|
-
const smsFallBackContent =
|
|
1133
|
-
smsFallbackTaggedTemplateBody.trim() !== ''
|
|
1134
|
-
? {
|
|
1135
|
-
message: smsFallbackTaggedTemplateBody,
|
|
1136
|
-
...(smsFallbackTemplateConfigsForApi && {
|
|
1137
|
-
templateConfigs: smsFallbackTemplateConfigsForApi,
|
|
1138
|
-
}),
|
|
1139
|
-
}
|
|
1140
|
-
: undefined;
|
|
1141
|
-
|
|
1142
|
-
// accountId: WeCRM account id (not sourceAccountIdentifier) for createMessageMeta
|
|
1143
|
-
const accountIdForMeta =
|
|
1144
|
-
rcsContentFromForm?.accountId != null && String(rcsContentFromForm.accountId).trim() !== ''
|
|
1145
|
-
? String(rcsContentFromForm.accountId)
|
|
1146
|
-
: undefined;
|
|
1147
|
-
|
|
1148
|
-
const rcsRichCardContent = {
|
|
1149
|
-
contentType: RCS_TEST_META_CONTENT_TYPE_RICHCARD,
|
|
1150
|
-
cardType: rcsContentFromForm?.cardType ?? RCS_TEST_META_CARD_TYPE_STANDALONE,
|
|
1151
|
-
cardSettings: rcsContentFromForm?.cardSettings ?? {
|
|
1152
|
-
cardOrientation: RCS_TEST_META_CARD_ORIENTATION_VERTICAL,
|
|
1153
|
-
cardWidth: RCS_TEST_META_CARD_WIDTH_SMALL,
|
|
1154
|
-
},
|
|
1155
|
-
...(cardContentForTestMetaApi.length > 0 && { cardContent: cardContentForTestMetaApi }),
|
|
1156
|
-
};
|
|
1157
|
-
|
|
1158
|
-
const rcsMessageContent = {
|
|
1159
|
-
channel: CHANNELS.RCS,
|
|
1160
|
-
...(accountIdForMeta && { accountId: accountIdForMeta }),
|
|
1161
|
-
rcsRichCardContent,
|
|
1162
|
-
...(smsFallBackContent && { smsFallBackContent }),
|
|
1163
|
-
};
|
|
1164
|
-
const rcsComposite = deliverySettingsOverride?.gsmSenderId ?? '';
|
|
1165
|
-
const [rcsDomainId, rcsSenderId] = rcsComposite.includes('|') ? rcsComposite.split('|') : ['', rcsComposite];
|
|
1166
|
-
const rcsDeliverySettings = {
|
|
1167
|
-
channelSettings: {
|
|
1168
|
-
channel: CHANNELS.RCS,
|
|
1169
|
-
rcsSender: (rcsSenderId || deliverySettingsOverride?.rcsSender) ?? '',
|
|
1170
|
-
domainId:
|
|
1171
|
-
rcsDomainId !== '' && rcsDomainId !== undefined && !Number.isNaN(Number(rcsDomainId))
|
|
1172
|
-
? Number(rcsDomainId)
|
|
1173
|
-
: (deliverySettingsOverride?.domainId ?? 0),
|
|
1174
|
-
fallbackSmsSenderId: fallbackSmsSenderIdForChannel,
|
|
1175
|
-
},
|
|
1176
|
-
additionalSettings: {
|
|
1177
|
-
useTinyUrl: false,
|
|
1178
|
-
encryptUrl: false,
|
|
1179
|
-
linkTrackingEnabled: false,
|
|
1180
|
-
bypassControlUser: false,
|
|
1181
|
-
userSubscriptionDisabled: false,
|
|
1182
|
-
},
|
|
1183
|
-
};
|
|
1184
|
-
const { clientName: baseClientName = CLIENT_NAME_CREATIVES, ...restBase } = basePayload;
|
|
1185
|
-
return {
|
|
1186
|
-
...restBase,
|
|
1187
|
-
rcsMessageContent,
|
|
1188
|
-
rcsDeliverySettings,
|
|
1189
|
-
executionParams: {},
|
|
1190
|
-
clientName: baseClientName,
|
|
1191
|
-
};
|
|
1192
|
-
};
|
|
1193
|
-
|
|
1194
|
-
const prepareTestMessagePayload = (
|
|
1195
|
-
channelType,
|
|
1196
|
-
formDataObj,
|
|
1197
|
-
contentStr,
|
|
1198
|
-
customValuesObj,
|
|
1199
|
-
recipientDetails,
|
|
1200
|
-
previewDataObj,
|
|
1201
|
-
deliverySettingsOverride,
|
|
1202
|
-
rcsExtra = {},
|
|
1203
|
-
) => {
|
|
769
|
+
const prepareTestMessagePayload = (channelType, formDataObj, contentStr, customValuesObj, recipientDetails, previewDataObj, deliverySettingsOverride) => {
|
|
1204
770
|
// Base payload structure common to all channels
|
|
1205
771
|
const basePayload = {
|
|
1206
772
|
ouId: -1,
|
|
@@ -1281,12 +847,12 @@ const CommonTestAndPreview = (props) => {
|
|
|
1281
847
|
},
|
|
1282
848
|
smsDeliverySettings: {
|
|
1283
849
|
channelSettings: {
|
|
1284
|
-
channel: CHANNELS.SMS,
|
|
1285
850
|
gsmSenderId: deliverySettingsOverride?.gsmSenderId ?? '',
|
|
1286
851
|
domainId: deliverySettingsOverride?.domainId ?? null,
|
|
1287
852
|
domainGatewayMapId: deliverySettingsOverride?.domainGatewayMapId ?? '',
|
|
1288
853
|
targetNdnc: false,
|
|
1289
854
|
cdmaSenderId: deliverySettingsOverride?.cdmaSenderId ?? '',
|
|
855
|
+
channel: CHANNELS.SMS,
|
|
1290
856
|
},
|
|
1291
857
|
additionalSettings: {
|
|
1292
858
|
useTinyUrl: false,
|
|
@@ -1430,7 +996,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
1430
996
|
return {
|
|
1431
997
|
...basePayload,
|
|
1432
998
|
whatsappMessageContent: {
|
|
1433
|
-
messageBody:
|
|
999
|
+
messageBody: templateEditorValue || '',
|
|
1434
1000
|
accountId: formDataObj?.accountId || '',
|
|
1435
1001
|
sourceAccountIdentifier: sourceAccountIdentifier || formDataObj?.sourceAccountIdentifier || '',
|
|
1436
1002
|
accountName: formDataObj?.accountName || '',
|
|
@@ -1455,7 +1021,16 @@ const CommonTestAndPreview = (props) => {
|
|
|
1455
1021
|
}
|
|
1456
1022
|
|
|
1457
1023
|
case CHANNELS.RCS:
|
|
1458
|
-
return
|
|
1024
|
+
return {
|
|
1025
|
+
...basePayload,
|
|
1026
|
+
rcsMessageContent: {
|
|
1027
|
+
channel: CHANNELS.RCS,
|
|
1028
|
+
messageBody: contentStr,
|
|
1029
|
+
rcsType: additionalProps?.rcsType,
|
|
1030
|
+
rcsImageSrc: formDataObj?.rcsImageSrc,
|
|
1031
|
+
rcsSuggestions: formDataObj?.rcsSuggestions,
|
|
1032
|
+
},
|
|
1033
|
+
};
|
|
1459
1034
|
|
|
1460
1035
|
case CHANNELS.INAPP: {
|
|
1461
1036
|
// InApp payload structure similar to MobilePush
|
|
@@ -2294,46 +1869,6 @@ const CommonTestAndPreview = (props) => {
|
|
|
2294
1869
|
*
|
|
2295
1870
|
* IMPORTANT: Use raw content/formData initially, only use previewDataHtml if preview call was made
|
|
2296
1871
|
*/
|
|
2297
|
-
// Memoized RCS carousel content: only recomputes when the API response or raw template changes,
|
|
2298
|
-
// NOT on every customValues keystroke. Uses previewCustomValuesRef captured at Update Preview time.
|
|
2299
|
-
const rcsCarouselContentObj = useMemo(() => {
|
|
2300
|
-
if (channel !== CHANNELS.RCS) return null;
|
|
2301
|
-
let resolvedContent = null;
|
|
2302
|
-
if (hasPreviewCallBeenMade && previewDataHtml?.resolvedBody) {
|
|
2303
|
-
resolvedContent = previewDataHtml.resolvedBody;
|
|
2304
|
-
if (typeof resolvedContent === 'string') {
|
|
2305
|
-
try { resolvedContent = JSON.parse(resolvedContent); } catch (e) { resolvedContent = null; }
|
|
2306
|
-
}
|
|
2307
|
-
}
|
|
2308
|
-
if (resolvedContent && typeof resolvedContent === 'object') return resolvedContent;
|
|
2309
|
-
|
|
2310
|
-
let rawRcs = content;
|
|
2311
|
-
if (typeof rawRcs === 'string') {
|
|
2312
|
-
try { rawRcs = JSON.parse(rawRcs); } catch (e) { rawRcs = content; }
|
|
2313
|
-
}
|
|
2314
|
-
const snapCustomValues = previewCustomValuesRef.current;
|
|
2315
|
-
const hasEnteredValues = snapCustomValues && Object.keys(snapCustomValues).some((k) => snapCustomValues[k]);
|
|
2316
|
-
if (hasPreviewCallBeenMade && hasEnteredValues && Array.isArray(rawRcs?.carouselData)) {
|
|
2317
|
-
const formDataCardContent =
|
|
2318
|
-
formData?.versions?.base?.content?.RCS?.rcsContent?.cardContent
|
|
2319
|
-
?? formData?.content?.RCS?.rcsContent?.cardContent
|
|
2320
|
-
?? [];
|
|
2321
|
-
return {
|
|
2322
|
-
...rawRcs,
|
|
2323
|
-
carouselData: rawRcs.carouselData.map((card, idx) => {
|
|
2324
|
-
const formCard = formDataCardContent[idx] || {};
|
|
2325
|
-
return {
|
|
2326
|
-
...card,
|
|
2327
|
-
title: resolveTagsInText(formCard.title || card.title || '', snapCustomValues),
|
|
2328
|
-
bodyText: resolveTagsInText(formCard.description || card.bodyText || '', snapCustomValues),
|
|
2329
|
-
};
|
|
2330
|
-
}),
|
|
2331
|
-
};
|
|
2332
|
-
}
|
|
2333
|
-
return rawRcs || {};
|
|
2334
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2335
|
-
}, [channel, content, hasPreviewCallBeenMade, previewDataHtml, formData]);
|
|
2336
|
-
|
|
2337
1872
|
const prepareUnifiedPreviewProps = () => {
|
|
2338
1873
|
// Prepare content based on channel
|
|
2339
1874
|
let contentObj = {};
|
|
@@ -2395,14 +1930,42 @@ const CommonTestAndPreview = (props) => {
|
|
|
2395
1930
|
contentObj = parsedContent || {};
|
|
2396
1931
|
}
|
|
2397
1932
|
} else if (channel === CHANNELS.RCS) {
|
|
2398
|
-
//
|
|
2399
|
-
//
|
|
2400
|
-
|
|
1933
|
+
// For RCS, content is an object with rcsTitle, rcsDesc, rcsImageSrc, suggestions, etc.
|
|
1934
|
+
// Content comes from RCS component state, passed via content prop
|
|
1935
|
+
let resolvedContent = null;
|
|
1936
|
+
|
|
1937
|
+
// Only use previewDataHtml if preview call was made
|
|
1938
|
+
if (hasPreviewCallBeenMade && previewDataHtml?.resolvedBody) {
|
|
1939
|
+
resolvedContent = previewDataHtml.resolvedBody;
|
|
1940
|
+
|
|
1941
|
+
// Handle case where resolvedBody might be a JSON string
|
|
1942
|
+
if (typeof resolvedContent === 'string') {
|
|
1943
|
+
try {
|
|
1944
|
+
resolvedContent = JSON.parse(resolvedContent);
|
|
1945
|
+
} catch (e) {
|
|
1946
|
+
// If parsing fails, treat as null so we fall back to content
|
|
1947
|
+
resolvedContent = null;
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
// Parse content if it's a string
|
|
2401
1953
|
let parsedRcsContent = content;
|
|
2402
1954
|
if (typeof content === 'string') {
|
|
2403
|
-
try {
|
|
1955
|
+
try {
|
|
1956
|
+
parsedRcsContent = JSON.parse(content);
|
|
1957
|
+
} catch (e) {
|
|
1958
|
+
parsedRcsContent = content;
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
// Use resolvedContent if available (from preview call), otherwise use raw content
|
|
1963
|
+
if (resolvedContent && typeof resolvedContent === 'object') {
|
|
1964
|
+
contentObj = { ...resolvedContent };
|
|
1965
|
+
} else {
|
|
1966
|
+
// Use raw content if no preview call was made or resolvedContent is not available
|
|
1967
|
+
contentObj = parsedRcsContent || {};
|
|
2404
1968
|
}
|
|
2405
|
-
contentObj = rcsCarouselContentObj || parsedRcsContent || {};
|
|
2406
1969
|
} else if (channel === CHANNELS.INAPP) {
|
|
2407
1970
|
// For InApp, content is an object with androidContent and iosContent (similar to MobilePush)
|
|
2408
1971
|
// Content comes from InApp component state, passed via content prop
|
|
@@ -2605,10 +2168,6 @@ const CommonTestAndPreview = (props) => {
|
|
|
2605
2168
|
formatMessage,
|
|
2606
2169
|
lastModified: formData?.lastModified,
|
|
2607
2170
|
updatedByName: formData?.updatedByName,
|
|
2608
|
-
smsFallbackContent: isRcsSmsFallbackPreviewEnabled ? smsFallbackContent : null,
|
|
2609
|
-
smsFallbackResolvedText,
|
|
2610
|
-
activePreviewTab,
|
|
2611
|
-
onPreviewTabChange: setActivePreviewTab,
|
|
2612
2171
|
};
|
|
2613
2172
|
};
|
|
2614
2173
|
|
|
@@ -2648,12 +2207,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
2648
2207
|
}, [show, beeInstance, currentTab, channel]);
|
|
2649
2208
|
|
|
2650
2209
|
/**
|
|
2651
|
-
* Initial data load when slidebox opens
|
|
2652
|
-
* EXTRACT TAGS CALL SITES (on open/edit RCS):
|
|
2653
|
-
* 1. Here (non-email): actions.extractTagsRequested() at line ~2161 when show is true.
|
|
2654
|
-
* 2. RCS SMS fallback useEffect below: Api.extractTagsWithMetaData() for fallback message.
|
|
2655
|
-
* 3. handleExtractTags() (user clicks "Enter custom values for tags") – not from effects.
|
|
2656
|
-
* The "Process extracted tags" effect only processes API results and must not call extract again.
|
|
2210
|
+
* Initial data load when slidebox opens
|
|
2657
2211
|
*/
|
|
2658
2212
|
useEffect(() => {
|
|
2659
2213
|
if (show) {
|
|
@@ -2738,61 +2292,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
2738
2292
|
actions.getTestGroupsRequested();
|
|
2739
2293
|
}
|
|
2740
2294
|
}
|
|
2741
|
-
|
|
2742
|
-
}, [show, beeInstance, currentTab, channel, getCurrentContent]);
|
|
2743
|
-
|
|
2744
|
-
/**
|
|
2745
|
-
* RCS with SMS fallback: extract tags for fallback SMS content as well
|
|
2746
|
-
* (so we can show a separate "Fallback SMS tags" section in left panel).
|
|
2747
|
-
*/
|
|
2748
|
-
useEffect(() => {
|
|
2749
|
-
let cancelled = false;
|
|
2750
|
-
|
|
2751
|
-
if (!show || channel !== CHANNELS.RCS) {
|
|
2752
|
-
return () => {
|
|
2753
|
-
cancelled = true;
|
|
2754
|
-
};
|
|
2755
|
-
}
|
|
2756
|
-
|
|
2757
|
-
if (!smsFallbackContent?.templateContent && !smsFallbackContent?.content) {
|
|
2758
|
-
setSmsFallbackExtractedTags([]);
|
|
2759
|
-
setSmsFallbackRequiredTags([]);
|
|
2760
|
-
setSmsFallbackOptionalTags([]);
|
|
2761
|
-
return () => {
|
|
2762
|
-
cancelled = true;
|
|
2763
|
-
};
|
|
2764
|
-
}
|
|
2765
|
-
|
|
2766
|
-
setIsExtractingSmsFallbackTags(true);
|
|
2767
|
-
(async () => {
|
|
2768
|
-
try {
|
|
2769
|
-
const fallbackBodyForExtractApi = getSmsFallbackTextForTagExtraction(smsFallbackContent);
|
|
2770
|
-
const payload = {
|
|
2771
|
-
messageTitle: '',
|
|
2772
|
-
messageBody: fallbackBodyForExtractApi || '',
|
|
2773
|
-
};
|
|
2774
|
-
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
|
|
2775
|
-
let smsFallbackTagTree = response?.data ?? [];
|
|
2776
|
-
if (!Array.isArray(smsFallbackTagTree)) smsFallbackTagTree = [];
|
|
2777
|
-
if (!smsTemplateHasMustacheTags(fallbackBodyForExtractApi)) {
|
|
2778
|
-
smsFallbackTagTree = [];
|
|
2779
|
-
} else if (smsFallbackTagTree.length === 0) {
|
|
2780
|
-
smsFallbackTagTree = buildSyntheticSmsMustacheTags(fallbackBodyForExtractApi);
|
|
2781
|
-
}
|
|
2782
|
-
if (cancelled) return;
|
|
2783
|
-
setSmsFallbackExtractedTags(smsFallbackTagTree);
|
|
2784
|
-
} catch (e) {
|
|
2785
|
-
if (cancelled) return;
|
|
2786
|
-
setSmsFallbackExtractedTags([]);
|
|
2787
|
-
} finally {
|
|
2788
|
-
if (!cancelled) setIsExtractingSmsFallbackTags(false);
|
|
2789
|
-
}
|
|
2790
|
-
})();
|
|
2791
|
-
|
|
2792
|
-
return () => {
|
|
2793
|
-
cancelled = true;
|
|
2794
|
-
};
|
|
2795
|
-
}, [show, channel, smsFallbackContent]);
|
|
2295
|
+
}, [show, beeInstance, currentTab, channel]);
|
|
2796
2296
|
|
|
2797
2297
|
/**
|
|
2798
2298
|
* Email-specific: Handle content updates for both BEE and CKEditor
|
|
@@ -2844,22 +2344,15 @@ const CommonTestAndPreview = (props) => {
|
|
|
2844
2344
|
setSelectedCustomer(null);
|
|
2845
2345
|
setRequiredTags([]);
|
|
2846
2346
|
setOptionalTags([]);
|
|
2847
|
-
setSmsFallbackExtractedTags([]);
|
|
2848
|
-
setSmsFallbackRequiredTags([]);
|
|
2849
|
-
setSmsFallbackOptionalTags([]);
|
|
2850
|
-
setIsExtractingSmsFallbackTags(false);
|
|
2851
2347
|
setCustomValues({});
|
|
2852
2348
|
setShowJSON(false);
|
|
2853
2349
|
setTagsExtracted(false);
|
|
2854
2350
|
setPreviewDevice(DESKTOP);
|
|
2855
|
-
setActivePreviewTab(PREVIEW_TAB_RCS);
|
|
2856
|
-
setSmsFallbackPreviewText(undefined);
|
|
2857
2351
|
setSelectedTestEntities([]);
|
|
2858
2352
|
actions.clearPrefilledValues();
|
|
2859
2353
|
} else {
|
|
2860
2354
|
// Reset device to initialDevice when opening (Android for mobile channels, Desktop for others)
|
|
2861
2355
|
setPreviewDevice(initialDevice);
|
|
2862
|
-
setActivePreviewTab(PREVIEW_TAB_RCS);
|
|
2863
2356
|
}
|
|
2864
2357
|
}, [show, initialDevice]);
|
|
2865
2358
|
|
|
@@ -2868,10 +2361,79 @@ const CommonTestAndPreview = (props) => {
|
|
|
2868
2361
|
*/
|
|
2869
2362
|
useEffect(() => {
|
|
2870
2363
|
if (previewData) {
|
|
2871
|
-
setPreviewDataHtml(
|
|
2364
|
+
setPreviewDataHtml(previewData);
|
|
2872
2365
|
}
|
|
2873
2366
|
}, [previewData]);
|
|
2874
2367
|
|
|
2368
|
+
/**
|
|
2369
|
+
* Process extracted tags and categorize them
|
|
2370
|
+
*/
|
|
2371
|
+
useEffect(() => {
|
|
2372
|
+
// Categorize tags into required and optional
|
|
2373
|
+
const required = [];
|
|
2374
|
+
const optional = [];
|
|
2375
|
+
let hasPersonalizationTags = false;
|
|
2376
|
+
|
|
2377
|
+
if (extractedTags?.length > 0) {
|
|
2378
|
+
const processTag = (tag, parentPath = '') => {
|
|
2379
|
+
const currentPath = parentPath ? `${parentPath}.${tag.name}` : tag.name;
|
|
2380
|
+
|
|
2381
|
+
// Skip unsubscribe tag for input fields
|
|
2382
|
+
if (tag?.name === UNSUBSCRIBE_TAG_NAME) {
|
|
2383
|
+
return;
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
hasPersonalizationTags = true;
|
|
2387
|
+
|
|
2388
|
+
if (tag?.metaData?.userDriven === false) {
|
|
2389
|
+
required.push({
|
|
2390
|
+
...tag,
|
|
2391
|
+
fullPath: currentPath,
|
|
2392
|
+
});
|
|
2393
|
+
} else if (tag?.metaData?.userDriven === true) {
|
|
2394
|
+
optional.push({
|
|
2395
|
+
...tag,
|
|
2396
|
+
fullPath: currentPath,
|
|
2397
|
+
});
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
if (tag?.children?.length > 0) {
|
|
2401
|
+
tag.children.forEach((child) => processTag(child, currentPath));
|
|
2402
|
+
}
|
|
2403
|
+
};
|
|
2404
|
+
|
|
2405
|
+
extractedTags.forEach((tag) => processTag(tag));
|
|
2406
|
+
|
|
2407
|
+
if (hasPersonalizationTags) {
|
|
2408
|
+
setRequiredTags(required);
|
|
2409
|
+
setOptionalTags(optional);
|
|
2410
|
+
setTagsExtracted(true); // Mark tags as extracted and processed
|
|
2411
|
+
|
|
2412
|
+
// Initialize custom values for required tags
|
|
2413
|
+
const initialValues = {};
|
|
2414
|
+
required.forEach((tag) => {
|
|
2415
|
+
initialValues[tag?.fullPath] = '';
|
|
2416
|
+
});
|
|
2417
|
+
optional.forEach((tag) => {
|
|
2418
|
+
initialValues[tag?.fullPath] = '';
|
|
2419
|
+
});
|
|
2420
|
+
setCustomValues(initialValues);
|
|
2421
|
+
} else {
|
|
2422
|
+
// Reset all tag-related state if no personalization tags
|
|
2423
|
+
setRequiredTags([]);
|
|
2424
|
+
setOptionalTags([]);
|
|
2425
|
+
setCustomValues({});
|
|
2426
|
+
setTagsExtracted(false);
|
|
2427
|
+
}
|
|
2428
|
+
} else {
|
|
2429
|
+
// Reset all tag-related state if no tags
|
|
2430
|
+
setRequiredTags([]);
|
|
2431
|
+
setOptionalTags([]);
|
|
2432
|
+
setCustomValues({});
|
|
2433
|
+
setTagsExtracted(false);
|
|
2434
|
+
}
|
|
2435
|
+
}, [extractedTags]);
|
|
2436
|
+
|
|
2875
2437
|
/**
|
|
2876
2438
|
* Handle customer selection and fetch prefilled values
|
|
2877
2439
|
*/
|
|
@@ -2879,15 +2441,17 @@ const CommonTestAndPreview = (props) => {
|
|
|
2879
2441
|
if (selectedCustomer && config.enableCustomerSearch !== false) {
|
|
2880
2442
|
setTagsExtracted(true); // Auto-open custom values editor
|
|
2881
2443
|
|
|
2444
|
+
// Get all available tags
|
|
2445
|
+
const allTags = [...requiredTags, ...optionalTags];
|
|
2882
2446
|
const requiredTagObj = {};
|
|
2883
|
-
|
|
2447
|
+
requiredTags.forEach((tag) => {
|
|
2884
2448
|
requiredTagObj[tag?.fullPath] = '';
|
|
2885
2449
|
});
|
|
2886
2450
|
if (allTags.length > 0) {
|
|
2887
2451
|
const payload = preparePreviewPayload(
|
|
2888
|
-
|
|
2452
|
+
channel,
|
|
2889
2453
|
formData || {},
|
|
2890
|
-
|
|
2454
|
+
getCurrentContent,
|
|
2891
2455
|
{
|
|
2892
2456
|
...requiredTagObj,
|
|
2893
2457
|
},
|
|
@@ -2896,7 +2460,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
2896
2460
|
actions.getPrefilledValuesRequested(payload);
|
|
2897
2461
|
}
|
|
2898
2462
|
}
|
|
2899
|
-
}, [selectedCustomer
|
|
2463
|
+
}, [selectedCustomer]);
|
|
2900
2464
|
|
|
2901
2465
|
/**
|
|
2902
2466
|
* Update custom values with prefilled values from API
|
|
@@ -2907,29 +2471,24 @@ const CommonTestAndPreview = (props) => {
|
|
|
2907
2471
|
if (prefilledValues && selectedCustomer) {
|
|
2908
2472
|
// Always replace all values with prefilled values
|
|
2909
2473
|
const updatedValues = {};
|
|
2910
|
-
|
|
2474
|
+
[...requiredTags, ...optionalTags].forEach((tag) => {
|
|
2911
2475
|
updatedValues[tag?.fullPath] = prefilledValues[tag?.fullPath] || '';
|
|
2912
2476
|
});
|
|
2913
2477
|
|
|
2914
2478
|
setCustomValues(updatedValues);
|
|
2915
2479
|
|
|
2916
2480
|
// Update preview with prefilled values (this is a valid preview call trigger)
|
|
2917
|
-
// For RCS: always dispatch with RCS channel/content so previewDataHtml stays RCS-shaped.
|
|
2918
|
-
// SMS fallback preview is kept in sync by syncSmsFallbackPreview via smsFallbackPreviewText.
|
|
2919
|
-
const previewChannelForPrefill = channel === CHANNELS.RCS ? CHANNELS.RCS : activeChannelForActions;
|
|
2920
|
-
const previewContentForPrefill = channel === CHANNELS.RCS ? getCurrentContent : activeContentForActions;
|
|
2921
2481
|
const payload = preparePreviewPayload(
|
|
2922
|
-
|
|
2482
|
+
channel,
|
|
2923
2483
|
formData || {},
|
|
2924
|
-
|
|
2484
|
+
getCurrentContent,
|
|
2925
2485
|
updatedValues,
|
|
2926
2486
|
selectedCustomer
|
|
2927
2487
|
);
|
|
2928
2488
|
actions.updatePreviewRequested(payload);
|
|
2929
2489
|
setHasPreviewCallBeenMade(true); // Mark that preview call was made
|
|
2930
|
-
void syncSmsFallbackPreview(updatedValues, selectedCustomer);
|
|
2931
2490
|
}
|
|
2932
|
-
}, [JSON.stringify(prefilledValues), selectedCustomer
|
|
2491
|
+
}, [JSON.stringify(prefilledValues), selectedCustomer]);
|
|
2933
2492
|
|
|
2934
2493
|
/**
|
|
2935
2494
|
* Map channel constants to display names (lowercase for message)
|
|
@@ -3023,7 +2582,11 @@ const CommonTestAndPreview = (props) => {
|
|
|
3023
2582
|
setTagsExtracted(true); // Auto-open custom values editor
|
|
3024
2583
|
|
|
3025
2584
|
// Clear any existing values while waiting for prefilled values
|
|
3026
|
-
|
|
2585
|
+
const emptyValues = {};
|
|
2586
|
+
[...requiredTags, ...optionalTags].forEach((tag) => {
|
|
2587
|
+
emptyValues[tag?.fullPath] = '';
|
|
2588
|
+
});
|
|
2589
|
+
setCustomValues(emptyValues);
|
|
3027
2590
|
};
|
|
3028
2591
|
|
|
3029
2592
|
/**
|
|
@@ -3037,7 +2600,11 @@ const CommonTestAndPreview = (props) => {
|
|
|
3037
2600
|
actions.clearPreviewErrors();
|
|
3038
2601
|
|
|
3039
2602
|
// Initialize empty values for all tags
|
|
3040
|
-
|
|
2603
|
+
const emptyValues = {};
|
|
2604
|
+
[...requiredTags, ...optionalTags].forEach((tag) => {
|
|
2605
|
+
emptyValues[tag?.fullPath] = '';
|
|
2606
|
+
});
|
|
2607
|
+
setCustomValues(emptyValues);
|
|
3041
2608
|
|
|
3042
2609
|
// Don't make preview call when clearing selection - just reset to raw content
|
|
3043
2610
|
// Preview will be shown using raw formData/content
|
|
@@ -3073,20 +2640,17 @@ const CommonTestAndPreview = (props) => {
|
|
|
3073
2640
|
*/
|
|
3074
2641
|
const handleDiscardCustomValues = () => {
|
|
3075
2642
|
// Initialize empty values for all tags
|
|
3076
|
-
const emptyValues =
|
|
2643
|
+
const emptyValues = {};
|
|
2644
|
+
[...requiredTags, ...optionalTags].forEach((tag) => {
|
|
2645
|
+
emptyValues[tag?.fullPath] = '';
|
|
2646
|
+
});
|
|
3077
2647
|
setCustomValues(emptyValues);
|
|
3078
2648
|
|
|
3079
|
-
// Reset SMS fallback preview so it shows raw template (with {{tags}} visible) after discard
|
|
3080
|
-
setSmsFallbackPreviewText(undefined);
|
|
3081
|
-
|
|
3082
2649
|
// Update preview with empty values (this is a valid preview call trigger)
|
|
3083
|
-
// For RCS: always dispatch with RCS channel/content so previewDataHtml stays RCS-shaped.
|
|
3084
|
-
const previewChannelForDiscard = channel === CHANNELS.RCS ? CHANNELS.RCS : activeChannelForActions;
|
|
3085
|
-
const previewContentForDiscard = channel === CHANNELS.RCS ? getCurrentContent : activeContentForActions;
|
|
3086
2650
|
const payload = preparePreviewPayload(
|
|
3087
|
-
|
|
2651
|
+
channel,
|
|
3088
2652
|
formData || {},
|
|
3089
|
-
|
|
2653
|
+
getCurrentContent,
|
|
3090
2654
|
emptyValues,
|
|
3091
2655
|
selectedCustomer
|
|
3092
2656
|
);
|
|
@@ -3100,24 +2664,14 @@ const CommonTestAndPreview = (props) => {
|
|
|
3100
2664
|
*/
|
|
3101
2665
|
const handleUpdatePreview = async () => {
|
|
3102
2666
|
try {
|
|
3103
|
-
// Capture customValues at click time so the carousel preview only updates here,
|
|
3104
|
-
// not on every subsequent keystroke.
|
|
3105
|
-
previewCustomValuesRef.current = customValues;
|
|
3106
|
-
// For RCS: always dispatch with RCS channel/content so previewDataHtml stays RCS-shaped,
|
|
3107
|
-
// even when the user triggers update from the SMS fallback tab.
|
|
3108
|
-
// SMS fallback preview is kept in sync by syncSmsFallbackPreview via smsFallbackPreviewText.
|
|
3109
|
-
const previewChannel = channel === CHANNELS.RCS ? CHANNELS.RCS : activeChannelForActions;
|
|
3110
|
-
const previewContent = channel === CHANNELS.RCS ? getCurrentContent : activeContentForActions;
|
|
3111
2667
|
const payload = preparePreviewPayload(
|
|
3112
|
-
|
|
2668
|
+
channel,
|
|
3113
2669
|
formData || {},
|
|
3114
|
-
|
|
2670
|
+
getCurrentContent,
|
|
3115
2671
|
customValues,
|
|
3116
2672
|
selectedCustomer
|
|
3117
2673
|
);
|
|
3118
2674
|
await actions.updatePreviewRequested(payload);
|
|
3119
|
-
|
|
3120
|
-
await syncSmsFallbackPreview(customValues, selectedCustomer);
|
|
3121
2675
|
setHasPreviewCallBeenMade(true); // Mark that preview call was made
|
|
3122
2676
|
} catch (error) {
|
|
3123
2677
|
CapNotification.error({
|
|
@@ -3127,115 +2681,25 @@ const CommonTestAndPreview = (props) => {
|
|
|
3127
2681
|
};
|
|
3128
2682
|
|
|
3129
2683
|
/**
|
|
3130
|
-
*
|
|
3131
|
-
*/
|
|
3132
|
-
const categorizeTags = (tagsTree = []) => {
|
|
3133
|
-
const required = [];
|
|
3134
|
-
const optional = [];
|
|
3135
|
-
let hasPersonalizationTags = false;
|
|
3136
|
-
const processTag = (tag, parentPath = '') => {
|
|
3137
|
-
const currentPath = parentPath ? `${parentPath}.${tag.name}` : tag.name;
|
|
3138
|
-
|
|
3139
|
-
// Skip unsubscribe tag for input fields
|
|
3140
|
-
if (tag?.name === UNSUBSCRIBE_TAG_NAME) return;
|
|
3141
|
-
|
|
3142
|
-
hasPersonalizationTags = true;
|
|
3143
|
-
const userDriven = tag?.metaData?.userDriven;
|
|
3144
|
-
if (userDriven === true) {
|
|
3145
|
-
optional.push({ ...tag, fullPath: currentPath });
|
|
3146
|
-
} else {
|
|
3147
|
-
// false or missing (SMS/DLT extract often omits metaData) → required for test values
|
|
3148
|
-
required.push({ ...tag, fullPath: currentPath });
|
|
3149
|
-
}
|
|
3150
|
-
|
|
3151
|
-
if (tag?.children?.length > 0) {
|
|
3152
|
-
tag.children.forEach((child) => processTag(child, currentPath));
|
|
3153
|
-
}
|
|
3154
|
-
};
|
|
3155
|
-
|
|
3156
|
-
(tagsTree || []).forEach((tag) => processTag(tag));
|
|
3157
|
-
return { required, optional, hasPersonalizationTags };
|
|
3158
|
-
};
|
|
3159
|
-
|
|
3160
|
-
/**
|
|
3161
|
-
* Apply tag extraction when content comes from RCS + SMS fallback (no API call).
|
|
3162
|
-
*/
|
|
3163
|
-
const applyRcsSmsFallbackTagExtraction = () => {
|
|
3164
|
-
const rcsPrimaryCategorized = categorizeTags(extractedTags ?? []);
|
|
3165
|
-
const fallbackSmsResolvedForTags = smsFallbackTextForTagExtraction ?? '';
|
|
3166
|
-
let fallbackSmsTagTree = smsFallbackExtractedTags?.length > 0
|
|
3167
|
-
? smsFallbackExtractedTags
|
|
3168
|
-
: buildSyntheticSmsMustacheTags(fallbackSmsResolvedForTags);
|
|
3169
|
-
if (!smsTemplateHasMustacheTags(fallbackSmsResolvedForTags)) {
|
|
3170
|
-
fallbackSmsTagTree = [];
|
|
3171
|
-
}
|
|
3172
|
-
const fallbackSmsCategorized = categorizeTags(fallbackSmsTagTree);
|
|
3173
|
-
setRequiredTags(rcsPrimaryCategorized.required);
|
|
3174
|
-
setOptionalTags(rcsPrimaryCategorized.optional);
|
|
3175
|
-
setSmsFallbackRequiredTags(fallbackSmsCategorized.required);
|
|
3176
|
-
setSmsFallbackOptionalTags(fallbackSmsCategorized.optional);
|
|
3177
|
-
setTagsExtracted(
|
|
3178
|
-
rcsPrimaryCategorized.hasPersonalizationTags || fallbackSmsCategorized.hasPersonalizationTags,
|
|
3179
|
-
);
|
|
3180
|
-
setCustomValues((prev) => mergeCustomValuesWithTagKeys(prev, rcsPrimaryCategorized, fallbackSmsCategorized));
|
|
3181
|
-
};
|
|
3182
|
-
|
|
3183
|
-
/**
|
|
3184
|
-
* When extract-tags API returns, map Redux `extractedTags` into required/optional so
|
|
3185
|
-
* CustomValuesEditor shows personalization fields (effect was previously commented out).
|
|
3186
|
-
* RCS + SMS fallback: merge primary + fallback tag trees when fallback template exists.
|
|
2684
|
+
* Handle extract tags
|
|
3187
2685
|
*/
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
if (channel === CHANNELS.RCS && hasFallbackSmsTemplate) {
|
|
3192
|
-
applyRcsSmsFallbackTagExtraction();
|
|
3193
|
-
return;
|
|
3194
|
-
}
|
|
3195
|
-
const smsEditorBody = typeof getCurrentContent === 'string' ? getCurrentContent : '';
|
|
3196
|
-
let smsTagSource = channel === CHANNELS.SMS && (!extractedTags || extractedTags.length === 0)
|
|
3197
|
-
? buildSyntheticSmsMustacheTags(smsEditorBody)
|
|
3198
|
-
: (extractedTags ?? []);
|
|
3199
|
-
if (channel === CHANNELS.SMS && !smsTemplateHasMustacheTags(smsEditorBody)) {
|
|
3200
|
-
smsTagSource = [];
|
|
3201
|
-
}
|
|
3202
|
-
const { required, optional, hasPersonalizationTags } = categorizeTags(smsTagSource);
|
|
3203
|
-
setRequiredTags(required);
|
|
3204
|
-
setOptionalTags(optional);
|
|
3205
|
-
setTagsExtracted(hasPersonalizationTags);
|
|
3206
|
-
setCustomValues((prev) => mergeCustomValuesWithTagKeys(prev, { required, optional }, { required: [], optional: [] }));
|
|
3207
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps -- applyRcsSmsFallbackTagExtraction closes over latest extractedTags/smsFallbackExtractedTags
|
|
3208
|
-
}, [show, extractedTags, channel, smsFallbackContent, smsFallbackExtractedTags, getCurrentContent, smsFallbackTextForTagExtraction]);
|
|
2686
|
+
const handleExtractTags = () => {
|
|
2687
|
+
// Get content based on channel
|
|
2688
|
+
let contentToExtract = getCurrentContent;
|
|
3209
2689
|
|
|
3210
|
-
/**
|
|
3211
|
-
* Get content to run tag extraction on (channel-specific).
|
|
3212
|
-
*/
|
|
3213
|
-
const getContentForTagExtraction = () => {
|
|
3214
|
-
let contentToExtract = activeContentForActions;
|
|
3215
2690
|
if (channel === CHANNELS.EMAIL && formData) {
|
|
3216
2691
|
const currentTabData = formData[currentTab - 1];
|
|
3217
2692
|
const activeTab = currentTabData?.activeTab;
|
|
3218
2693
|
const templateContent = currentTabData?.[activeTab]?.['template-content'];
|
|
3219
2694
|
contentToExtract = templateContent || contentToExtract;
|
|
3220
2695
|
}
|
|
3221
|
-
return contentToExtract;
|
|
3222
|
-
};
|
|
3223
|
-
|
|
3224
|
-
/**
|
|
3225
|
-
* Handle extract tags
|
|
3226
|
-
*/
|
|
3227
|
-
const handleExtractTags = () => {
|
|
3228
|
-
if (channel === CHANNELS.RCS) {
|
|
3229
|
-
applyRcsSmsFallbackTagExtraction();
|
|
3230
|
-
return;
|
|
3231
|
-
}
|
|
3232
2696
|
|
|
3233
|
-
|
|
2697
|
+
// Check for personalization tags (excluding unsubscribe)
|
|
3234
2698
|
const tags = contentToExtract.match(/{{[^}]+}}/g) || [];
|
|
3235
2699
|
const hasPersonalizationTags = tags.some((tag) => !tag.includes(UNSUBSCRIBE_TAG_NAME));
|
|
3236
|
-
const onlyUnsubscribe = !hasPersonalizationTags && tags.length === 1 && tags[0].includes(UNSUBSCRIBE_TAG_NAME);
|
|
3237
2700
|
|
|
3238
|
-
if (
|
|
2701
|
+
if (!hasPersonalizationTags && tags.length === 1 && tags[0].includes(UNSUBSCRIBE_TAG_NAME)) {
|
|
2702
|
+
// If only unsubscribe tag is present, show noTagsExtracted message
|
|
3239
2703
|
setTagsExtracted(false);
|
|
3240
2704
|
setRequiredTags([]);
|
|
3241
2705
|
setOptionalTags([]);
|
|
@@ -3243,9 +2707,10 @@ const CommonTestAndPreview = (props) => {
|
|
|
3243
2707
|
return;
|
|
3244
2708
|
}
|
|
3245
2709
|
|
|
2710
|
+
// Extract tags
|
|
3246
2711
|
setTagsExtracted(true);
|
|
3247
2712
|
const { templateSubject, templateContent } = prepareTagExtractionPayload(
|
|
3248
|
-
|
|
2713
|
+
channel,
|
|
3249
2714
|
formData || {},
|
|
3250
2715
|
contentToExtract
|
|
3251
2716
|
);
|
|
@@ -3307,7 +2772,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
3307
2772
|
if (existingTestCustomer) {
|
|
3308
2773
|
const entityId = existingTestCustomer.userId ?? existingTestCustomer.customerId;
|
|
3309
2774
|
if (entityId != null) {
|
|
3310
|
-
const id = normalizeTestEntityId(entityId);
|
|
2775
|
+
const id = 'customer:' + normalizeTestEntityId(entityId);
|
|
3311
2776
|
setSelectedTestEntities((prev) => (
|
|
3312
2777
|
prev.some((existing) => testEntityIdsEqual(existing, id)) ? prev : [...prev, id]
|
|
3313
2778
|
));
|
|
@@ -3344,7 +2809,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
3344
2809
|
(c) => String(c?.customerId) === customerIdFromLookup || String(c?.userId) === customerIdFromLookup
|
|
3345
2810
|
);
|
|
3346
2811
|
if (alreadyInTestListByCustomerId) {
|
|
3347
|
-
const id = normalizeTestEntityId(customerIdFromLookup);
|
|
2812
|
+
const id = 'customer:' + normalizeTestEntityId(customerIdFromLookup);
|
|
3348
2813
|
setSelectedTestEntities((prev) => (
|
|
3349
2814
|
prev.some((existing) => testEntityIdsEqual(existing, id)) ? prev : [...prev, id]
|
|
3350
2815
|
));
|
|
@@ -3381,21 +2846,26 @@ const CommonTestAndPreview = (props) => {
|
|
|
3381
2846
|
const handleSendTestMessage = () => {
|
|
3382
2847
|
const allUserIds = [];
|
|
3383
2848
|
selectedTestEntities.forEach((entityId) => {
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
2849
|
+
if (String(entityId).startsWith('group:')) {
|
|
2850
|
+
const rawId = String(entityId).slice('group:'.length);
|
|
2851
|
+
const group = testGroups.find((g) => testEntityIdsEqual(g.groupId, rawId));
|
|
2852
|
+
if (group) {
|
|
2853
|
+
allUserIds.push(...group.userIds);
|
|
2854
|
+
}
|
|
3387
2855
|
} else {
|
|
3388
|
-
|
|
2856
|
+
const rawId = String(entityId).startsWith('customer:')
|
|
2857
|
+
? String(entityId).slice('customer:'.length)
|
|
2858
|
+
: String(entityId);
|
|
2859
|
+
allUserIds.push(Number(rawId));
|
|
3389
2860
|
}
|
|
3390
2861
|
});
|
|
3391
2862
|
const uniqueUserIds = [...new Set(allUserIds)];
|
|
3392
2863
|
|
|
3393
|
-
const deliveryOverride = [CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP
|
|
2864
|
+
const deliveryOverride = [CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP].includes(channel)
|
|
3394
2865
|
? testPreviewDeliverySettings[channel]
|
|
3395
2866
|
: null;
|
|
3396
2867
|
|
|
3397
|
-
//
|
|
3398
|
-
// Do not use activeChannelForActions / activeContentForActions — those follow the RCS vs Fallback SMS *preview* tab.
|
|
2868
|
+
// Create initial payload based on channel
|
|
3399
2869
|
const initialPayload = prepareTestMessagePayload(
|
|
3400
2870
|
channel,
|
|
3401
2871
|
formData || content || {},
|
|
@@ -3403,8 +2873,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
3403
2873
|
customValues,
|
|
3404
2874
|
uniqueUserIds,
|
|
3405
2875
|
previewData,
|
|
3406
|
-
deliveryOverride
|
|
3407
|
-
{},
|
|
2876
|
+
deliveryOverride
|
|
3408
2877
|
);
|
|
3409
2878
|
|
|
3410
2879
|
actions.createMessageMetaRequested(
|
|
@@ -3437,10 +2906,11 @@ const CommonTestAndPreview = (props) => {
|
|
|
3437
2906
|
// ============================================
|
|
3438
2907
|
// RENDER HELPER FUNCTIONS
|
|
3439
2908
|
// ============================================
|
|
2909
|
+
|
|
3440
2910
|
const renderLeftPanelContent = () => (
|
|
3441
2911
|
<LeftPanelContent
|
|
3442
|
-
isExtractingTags={isExtractingTags
|
|
3443
|
-
extractedTags={
|
|
2912
|
+
isExtractingTags={isExtractingTags}
|
|
2913
|
+
extractedTags={extractedTags}
|
|
3444
2914
|
selectedCustomer={selectedCustomer}
|
|
3445
2915
|
handleCustomerSelect={handleCustomerSelect}
|
|
3446
2916
|
handleSearchCustomer={handleSearchCustomer}
|
|
@@ -3458,29 +2928,15 @@ const CommonTestAndPreview = (props) => {
|
|
|
3458
2928
|
|
|
3459
2929
|
const renderCustomValuesEditor = () => (
|
|
3460
2930
|
<CustomValuesEditor
|
|
3461
|
-
isExtractingTags={isExtractingTags
|
|
2931
|
+
isExtractingTags={isExtractingTags}
|
|
3462
2932
|
isUpdatePreviewDisabled={isUpdatePreviewDisabled}
|
|
3463
2933
|
showJSON={showJSON}
|
|
3464
2934
|
setShowJSON={setShowJSON}
|
|
3465
2935
|
customValues={customValues}
|
|
3466
2936
|
handleJSONTextChange={handleJSONTextChange}
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
title:
|
|
3471
|
-
channel === CHANNELS.RCS
|
|
3472
|
-
? messages.rcsTagsSectionTitle
|
|
3473
|
-
: messages[`${channel}TagsSectionTitle`],
|
|
3474
|
-
requiredTags,
|
|
3475
|
-
optionalTags,
|
|
3476
|
-
},
|
|
3477
|
-
{
|
|
3478
|
-
key: PREVIEW_TAB_SMS_FALLBACK,
|
|
3479
|
-
title: isRcsSmsFallbackPreviewEnabled ? messages.smsFallbackTagsSectionTitle : null,
|
|
3480
|
-
requiredTags: smsFallbackRequiredTags,
|
|
3481
|
-
optionalTags: smsFallbackOptionalTags,
|
|
3482
|
-
},
|
|
3483
|
-
]}
|
|
2937
|
+
extractedTags={extractedTags}
|
|
2938
|
+
requiredTags={requiredTags}
|
|
2939
|
+
optionalTags={optionalTags}
|
|
3484
2940
|
handleCustomValueChange={handleCustomValueChange}
|
|
3485
2941
|
handleDiscardCustomValues={handleDiscardCustomValues}
|
|
3486
2942
|
handleUpdatePreview={handleUpdatePreview}
|
|
@@ -3496,13 +2952,18 @@ const CommonTestAndPreview = (props) => {
|
|
|
3496
2952
|
}));
|
|
3497
2953
|
};
|
|
3498
2954
|
|
|
3499
|
-
/** Trim pasted emails (trailing CR/LF).
|
|
2955
|
+
/** Trim pasted emails (trailing CR/LF). SMS: strip non-digits so pasted formatted numbers match isValidMobile / API. */
|
|
3500
2956
|
const handleTestCustomersSearch = useCallback((value) => {
|
|
3501
2957
|
if (value == null || value === '') {
|
|
3502
2958
|
setSearchValue('');
|
|
3503
2959
|
return;
|
|
3504
2960
|
}
|
|
3505
|
-
|
|
2961
|
+
const raw = String(value).trim();
|
|
2962
|
+
if (channel === CHANNELS.SMS) {
|
|
2963
|
+
setSearchValue(formatPhoneNumber(raw));
|
|
2964
|
+
} else {
|
|
2965
|
+
setSearchValue(raw);
|
|
2966
|
+
}
|
|
3506
2967
|
}, [channel]);
|
|
3507
2968
|
|
|
3508
2969
|
const renderSendTestMessage = () => (
|
|
@@ -3520,13 +2981,12 @@ const CommonTestAndPreview = (props) => {
|
|
|
3520
2981
|
renderAddTestCustomerButton={renderAddTestCustomerButton}
|
|
3521
2982
|
formatMessage={formatMessage}
|
|
3522
2983
|
deliverySettings={testPreviewDeliverySettings[channel]}
|
|
3523
|
-
|
|
2984
|
+
senderDetailsOptions={senderDetailsByChannel[channel]}
|
|
3524
2985
|
wecrmAccounts={wecrmAccounts}
|
|
3525
2986
|
onSaveDeliverySettings={handleSaveDeliverySettings}
|
|
3526
2987
|
isLoadingSenderDetails={isLoadingSenderDetails}
|
|
3527
2988
|
smsTraiDltEnabled={smsTraiDltEnabled}
|
|
3528
2989
|
registeredSenderIds={registeredSenderIds}
|
|
3529
|
-
isChannelSmsFallbackPreviewEnabled={isRcsSmsFallbackPreviewEnabled}
|
|
3530
2990
|
searchValue={searchValue}
|
|
3531
2991
|
setSearchValue={handleTestCustomersSearch}
|
|
3532
2992
|
/>
|
|
@@ -3540,13 +3000,14 @@ const CommonTestAndPreview = (props) => {
|
|
|
3540
3000
|
|
|
3541
3001
|
const renderAddTestCustomerButton = () => {
|
|
3542
3002
|
const raw = (searchValue || '').trim();
|
|
3003
|
+
const value = channel === CHANNELS.SMS ? formatPhoneNumber(raw) : raw;
|
|
3543
3004
|
const showAddButton =
|
|
3544
3005
|
[CHANNELS.EMAIL, CHANNELS.SMS].includes(channel) &&
|
|
3545
|
-
(channel === CHANNELS.EMAIL ? isValidEmail(
|
|
3006
|
+
(channel === CHANNELS.EMAIL ? isValidEmail(value) : isValidMobile(value));
|
|
3546
3007
|
if (!showAddButton) return null;
|
|
3547
3008
|
return (
|
|
3548
3009
|
<AddTestCustomerButton
|
|
3549
|
-
searchValue={
|
|
3010
|
+
searchValue={value}
|
|
3550
3011
|
handleAddTestCustomer={handleAddTestCustomer}
|
|
3551
3012
|
/>
|
|
3552
3013
|
);
|