@capillarytech/creatives-library 8.0.353-alpha.6 → 8.0.353
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/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 +108 -166
- package/v2Components/CapActionButton/index.scss +6 -157
- package/v2Components/CapActionButton/messages.js +3 -19
- package/v2Components/CapActionButton/tests/index.test.js +17 -41
- package/v2Components/CapTagList/index.js +0 -10
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +49 -72
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -213
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +10 -85
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -79
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +5 -10
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +15 -157
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +76 -346
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +4 -133
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +0 -11
- package/v2Components/CommonTestAndPreview/constants.js +2 -38
- package/v2Components/CommonTestAndPreview/index.js +186 -691
- package/v2Components/CommonTestAndPreview/messages.js +3 -45
- package/v2Components/CommonTestAndPreview/sagas.js +6 -25
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +284 -308
- 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 -132
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +26 -36
- package/v2Components/FormBuilder/index.js +168 -63
- package/v2Components/TemplatePreview/_templatePreview.scss +23 -38
- package/v2Components/TemplatePreview/index.js +31 -143
- package/v2Components/TemplatePreview/tests/index.test.js +0 -142
- 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/CreativesContainer/SlideBoxContent.js +4 -36
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +1 -10
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +4 -29
- package/v2Containers/CreativesContainer/constants.js +0 -9
- package/v2Containers/CreativesContainer/index.js +163 -346
- package/v2Containers/CreativesContainer/index.scss +1 -51
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -78
- 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 +98 -357
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +15 -20
- package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
- package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
- package/v2Containers/Rcs/constants.js +10 -119
- package/v2Containers/Rcs/index.js +818 -2450
- package/v2Containers/Rcs/index.scss +8 -280
- package/v2Containers/Rcs/messages.js +3 -34
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +70073 -98018
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +5 -0
- package/v2Containers/Rcs/tests/index.test.js +121 -152
- 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 +40 -106
- package/v2Containers/SmsTrai/Create/index.js +4 -9
- package/v2Containers/SmsTrai/Edit/constants.js +0 -2
- package/v2Containers/SmsTrai/Edit/index.js +130 -640
- package/v2Containers/SmsTrai/Edit/messages.js +4 -14
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2296 -4249
- package/v2Containers/SmsWrapper/index.js +8 -37
- package/v2Containers/TagList/index.js +0 -6
- package/v2Containers/Templates/_templates.scss +9 -166
- package/v2Containers/Templates/actions.js +0 -11
- package/v2Containers/Templates/constants.js +0 -2
- package/v2Containers/Templates/index.js +52 -120
- package/v2Containers/Templates/sagas.js +12 -56
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1017 -1062
- 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/SmsFallback/SmsFallbackLocalSelector.js +0 -91
- package/v2Components/SmsFallback/constants.js +0 -73
- package/v2Components/SmsFallback/index.js +0 -956
- package/v2Components/SmsFallback/index.scss +0 -265
- package/v2Components/SmsFallback/messages.js +0 -78
- package/v2Components/SmsFallback/smsFallbackUtils.js +0 -119
- 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 -223
- 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 -43
- 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 -225
- 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 -121
- 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,8 +31,7 @@ import CustomValuesEditor from './CustomValuesEditor';
|
|
|
31
31
|
import SendTestMessage from './SendTestMessage';
|
|
32
32
|
import PreviewSection from './PreviewSection';
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
import { extractTemplateVariables } from '../../utils/templateVarUtils';
|
|
34
|
+
// Import constants
|
|
36
35
|
import AddTestCustomerButton from './AddTestCustomer';
|
|
37
36
|
import ExistingCustomerModal from './ExistingCustomerModal';
|
|
38
37
|
// Import constants
|
|
@@ -82,23 +81,11 @@ import {
|
|
|
82
81
|
IMAGE,
|
|
83
82
|
VIDEO,
|
|
84
83
|
URL,
|
|
85
|
-
|
|
86
|
-
PREVIEW_TAB_SMS_FALLBACK,
|
|
87
|
-
CHANNELS_USING_ANDROID_PREVIEW_DEVICE,
|
|
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,
|
|
84
|
+
CHANNELS_USING_ANDROID_PREVIEW_DEVICE
|
|
93
85
|
} from './constants';
|
|
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
86
|
|
|
87
|
+
// Import utilities
|
|
88
|
+
import { getCdnUrl } from '../../utils/cdnTransformation';
|
|
102
89
|
import { isValidEmail, isValidMobile, formatPhoneNumber } from '../../utils/commonUtils';
|
|
103
90
|
import { getMembersLookup } from '../../services/api';
|
|
104
91
|
|
|
@@ -121,85 +108,6 @@ const filterUsableGsmSendersForDomain = (domain, gsmSenders, { skipDomainNameEch
|
|
|
121
108
|
});
|
|
122
109
|
};
|
|
123
110
|
|
|
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
|
-
});
|
|
203
111
|
|
|
204
112
|
/**
|
|
205
113
|
* CapTreeSelect and group resolution use strict equality; API data may mix numeric and string ids.
|
|
@@ -225,7 +133,7 @@ const testEntityIdsEqual = (a, b) => {
|
|
|
225
133
|
*/
|
|
226
134
|
const CommonTestAndPreview = (props) => {
|
|
227
135
|
const {
|
|
228
|
-
intl: { formatMessage
|
|
136
|
+
intl: { formatMessage },
|
|
229
137
|
show,
|
|
230
138
|
onClose,
|
|
231
139
|
channel, // The channel: 'EMAIL', 'SMS', 'RCS', etc.
|
|
@@ -261,21 +169,12 @@ const CommonTestAndPreview = (props) => {
|
|
|
261
169
|
...additionalProps
|
|
262
170
|
} = props;
|
|
263
171
|
|
|
264
|
-
const smsFallbackContent = additionalProps?.smsFallbackContent;
|
|
265
|
-
const smsFallbackTextForTagExtraction = useMemo(
|
|
266
|
-
() => getSmsFallbackTextForTagExtraction(smsFallbackContent),
|
|
267
|
-
[smsFallbackContent],
|
|
268
|
-
);
|
|
269
172
|
// ============================================
|
|
270
173
|
// STATE MANAGEMENT
|
|
271
174
|
// ============================================
|
|
272
175
|
const [selectedCustomer, setSelectedCustomer] = useState(null);
|
|
273
176
|
const [requiredTags, setRequiredTags] = useState([]);
|
|
274
177
|
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);
|
|
279
178
|
const [customValues, setCustomValues] = useState({});
|
|
280
179
|
const [showJSON, setShowJSON] = useState(false);
|
|
281
180
|
const [tagsExtracted, setTagsExtracted] = useState(false);
|
|
@@ -287,8 +186,6 @@ const CommonTestAndPreview = (props) => {
|
|
|
287
186
|
const [customerData, setCustomerData] = useState({ name: '', email: '', mobile: '', customerId: '' });
|
|
288
187
|
|
|
289
188
|
const [previewDevice, setPreviewDevice] = useState(initialDevice);
|
|
290
|
-
const [activePreviewTab, setActivePreviewTab] = useState(PREVIEW_TAB_RCS);
|
|
291
|
-
const [smsFallbackPreviewText, setSmsFallbackPreviewText] = useState(undefined);
|
|
292
189
|
// Track if a preview call has been made (to know when to use previewDataHtml vs raw content)
|
|
293
190
|
const [hasPreviewCallBeenMade, setHasPreviewCallBeenMade] = useState(false);
|
|
294
191
|
const [previewDataHtml, setPreviewDataHtml] = useState(() => {
|
|
@@ -321,22 +218,15 @@ const CommonTestAndPreview = (props) => {
|
|
|
321
218
|
[CHANNELS.WHATSAPP]: {
|
|
322
219
|
domainId: null, senderMobNum: '', sourceAccountIdentifier: '',
|
|
323
220
|
},
|
|
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
|
-
},
|
|
331
221
|
});
|
|
332
222
|
|
|
333
|
-
const channelsWithDeliverySettings = [CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP
|
|
223
|
+
const channelsWithDeliverySettings = [CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP];
|
|
334
224
|
const formDataForSendTest = formData ?? (content && typeof content === 'object' && !Array.isArray(content) ? content : formData);
|
|
335
225
|
const smsTemplateConfigs = formDataForSendTest?.templateConfigs || {};
|
|
336
226
|
const smsTraiDltEnabled = !!smsTemplateConfigs?.traiDltEnabled;
|
|
337
227
|
const registeredSenderIds = smsTemplateConfigs?.registeredSenderIds || [];
|
|
338
228
|
|
|
339
|
-
// Fetch sender details and WeCRM accounts when Test & Preview opens
|
|
229
|
+
// Fetch sender details and WeCRM accounts when Test & Preview opens for SMS/Email/WhatsApp
|
|
340
230
|
useEffect(() => {
|
|
341
231
|
if (!show || !channel) {
|
|
342
232
|
return;
|
|
@@ -345,99 +235,39 @@ const CommonTestAndPreview = (props) => {
|
|
|
345
235
|
if (actions.getSenderDetailsRequested) {
|
|
346
236
|
actions.getSenderDetailsRequested({ channel, orgUnitId: orgUnitId ?? -1 });
|
|
347
237
|
}
|
|
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
|
-
}
|
|
352
238
|
if (channel === CHANNELS.WHATSAPP && actions.getWeCrmAccountsRequested) {
|
|
353
239
|
actions.getWeCrmAccountsRequested({ sourceName: CHANNELS.WHATSAPP });
|
|
354
240
|
}
|
|
355
241
|
}
|
|
356
242
|
}, [show, channel, orgUnitId, actions]);
|
|
357
243
|
|
|
244
|
+
useEffect(() => {
|
|
245
|
+
if (!show) {
|
|
246
|
+
setCustomerModal([false, '']);
|
|
247
|
+
setSearchValue('');
|
|
248
|
+
setCustomerData({ name: '', email: '', mobile: '', customerId: '' });
|
|
249
|
+
}
|
|
250
|
+
}, [show]);
|
|
251
|
+
|
|
358
252
|
const findDefault = (arr) => (arr && arr.find((x) => x.default)) || (arr && arr[0]) || {};
|
|
359
253
|
|
|
360
254
|
// Auto-set default delivery setting when sender details load (campaigns-style: first domain + default/first sender)
|
|
361
255
|
useEffect(() => {
|
|
362
256
|
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
|
-
|
|
426
257
|
const domains = senderDetailsByChannel[channel];
|
|
427
258
|
if (!domains || domains.length === 0) return;
|
|
428
259
|
const {
|
|
429
|
-
domainId = '', gsmSenderId = '',
|
|
260
|
+
domainId = '', gsmSenderId = '', senderEmail = '', senderMobNum = '',
|
|
430
261
|
} = testPreviewDeliverySettings[channel] || {};
|
|
431
|
-
const isEmptySelection = !domainId && !gsmSenderId && !
|
|
262
|
+
const isEmptySelection = !domainId && !gsmSenderId && !senderEmail && !senderMobNum;
|
|
432
263
|
if (!isEmptySelection) return;
|
|
433
264
|
|
|
434
265
|
const whatsappAccountFromForm = channel === CHANNELS.WHATSAPP ? formData?.accountName : undefined;
|
|
435
266
|
const matchedWhatsappAccount = whatsappAccountFromForm
|
|
436
267
|
? (wecrmAccounts || []).find((account) => account?.name === whatsappAccountFromForm)
|
|
437
268
|
: null;
|
|
438
|
-
const smsDomains = channel === CHANNELS.SMS && smsTraiDltEnabled
|
|
439
|
-
? domains.filter((domain) => (domain
|
|
440
|
-
registeredSenderIds?.includes(gsm?.value)))
|
|
269
|
+
const smsDomains = channel === CHANNELS.SMS && smsTraiDltEnabled
|
|
270
|
+
? domains.filter((domain) => (domain.gsmSenders || []).some((gsm) => registeredSenderIds.includes(gsm.value)))
|
|
441
271
|
: domains;
|
|
442
272
|
const [defaultDomain] = domains;
|
|
443
273
|
const [firstSmsDomain] = smsDomains;
|
|
@@ -452,8 +282,8 @@ const CommonTestAndPreview = (props) => {
|
|
|
452
282
|
const next = { ...prev };
|
|
453
283
|
if (channel === CHANNELS.SMS) {
|
|
454
284
|
const smsGsmSenders = smsTraiDltEnabled
|
|
455
|
-
? (firstDomain
|
|
456
|
-
: firstDomain
|
|
285
|
+
? (firstDomain.gsmSenders || []).filter((gsm) => registeredSenderIds.includes(gsm.value))
|
|
286
|
+
: firstDomain.gsmSenders;
|
|
457
287
|
next[channel] = {
|
|
458
288
|
domainId: firstDomain.domainId,
|
|
459
289
|
domainGatewayMapId: firstDomain.dgmId,
|
|
@@ -486,29 +316,10 @@ const CommonTestAndPreview = (props) => {
|
|
|
486
316
|
// MEMOIZED VALUES
|
|
487
317
|
// ============================================
|
|
488
318
|
|
|
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
|
-
|
|
508
319
|
// Check if update preview button should be disabled
|
|
509
320
|
const isUpdatePreviewDisabled = useMemo(() => (
|
|
510
|
-
|
|
511
|
-
), [
|
|
321
|
+
requiredTags.some((tag) => !customValues[tag.fullPath])
|
|
322
|
+
), [requiredTags, customValues]);
|
|
512
323
|
|
|
513
324
|
// Get current content based on channel and editor type
|
|
514
325
|
const getCurrentContent = useMemo(() => {
|
|
@@ -552,13 +363,6 @@ const CommonTestAndPreview = (props) => {
|
|
|
552
363
|
return currentTabData.base['sms-editor'];
|
|
553
364
|
}
|
|
554
365
|
}
|
|
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
|
-
}
|
|
562
366
|
}
|
|
563
367
|
|
|
564
368
|
// SMS channel fallback - if formData is not available, use content directly
|
|
@@ -604,70 +408,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
604
408
|
return content || '';
|
|
605
409
|
}, [channel, formData, currentTab, beeContent, content, beeInstance]);
|
|
606
410
|
|
|
607
|
-
|
|
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
|
|
411
|
+
// Build test entities tree data
|
|
671
412
|
// Build test entities tree data from testCustomers prop (includes customers added via addTestCustomer action)
|
|
672
413
|
const testEntitiesTreeData = useMemo(() => {
|
|
673
414
|
const groupsNode = {
|
|
@@ -676,7 +417,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
676
417
|
selectable: false,
|
|
677
418
|
children: testGroups?.map((group) => ({
|
|
678
419
|
title: group?.groupName,
|
|
679
|
-
value: normalizeTestEntityId(group?.groupId),
|
|
420
|
+
value: 'group:' + normalizeTestEntityId(group?.groupId),
|
|
680
421
|
})),
|
|
681
422
|
};
|
|
682
423
|
|
|
@@ -686,7 +427,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
686
427
|
selectable: false,
|
|
687
428
|
children: testCustomers?.map((customer) => ({
|
|
688
429
|
title: customer?.name?.trim() || customer?.email?.trim() || customer?.mobile?.trim() || customer?.userId || customer?.customerId,
|
|
689
|
-
value: normalizeTestEntityId(customer?.userId ?? customer?.customerId),
|
|
430
|
+
value: 'customer:' + normalizeTestEntityId(customer?.userId ?? customer?.customerId),
|
|
690
431
|
})) || [],
|
|
691
432
|
};
|
|
692
433
|
|
|
@@ -766,10 +507,11 @@ const CommonTestAndPreview = (props) => {
|
|
|
766
507
|
email: customerData?.email || '',
|
|
767
508
|
mobile: customerData?.mobile || '',
|
|
768
509
|
});
|
|
510
|
+
const prefixedAddedId = 'customer:' + normalizedAddedId;
|
|
769
511
|
setSelectedTestEntities((prev) => (
|
|
770
|
-
prev.some((id) =>
|
|
512
|
+
prev.some((id) => id === prefixedAddedId)
|
|
771
513
|
? prev
|
|
772
|
-
: [...prev,
|
|
514
|
+
: [...prev, prefixedAddedId]
|
|
773
515
|
));
|
|
774
516
|
}
|
|
775
517
|
handleCloseCustomerModal();
|
|
@@ -873,37 +615,6 @@ const CommonTestAndPreview = (props) => {
|
|
|
873
615
|
}
|
|
874
616
|
};
|
|
875
617
|
|
|
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
|
-
|
|
907
618
|
/**
|
|
908
619
|
* Prepare payload for tag extraction based on channel
|
|
909
620
|
*/
|
|
@@ -998,7 +709,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
998
709
|
} = carousel || {};
|
|
999
710
|
const buttonData = buttons.map((button, index) => {
|
|
1000
711
|
const {
|
|
1001
|
-
type, text, phone_number
|
|
712
|
+
type, text, phone_number, urlType, url,
|
|
1002
713
|
} = button || {};
|
|
1003
714
|
const buttonObj = {
|
|
1004
715
|
type,
|
|
@@ -1006,7 +717,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
1006
717
|
index,
|
|
1007
718
|
};
|
|
1008
719
|
if (type === PHONE_NUMBER) {
|
|
1009
|
-
buttonObj.phoneNumber =
|
|
720
|
+
buttonObj.phoneNumber = phone_number;
|
|
1010
721
|
}
|
|
1011
722
|
if (type === URL) {
|
|
1012
723
|
buttonObj.url = url;
|
|
@@ -1035,133 +746,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
1035
746
|
};
|
|
1036
747
|
});
|
|
1037
748
|
|
|
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
|
-
) => {
|
|
749
|
+
const prepareTestMessagePayload = (channelType, formDataObj, contentStr, customValuesObj, recipientDetails, previewDataObj, deliverySettingsOverride) => {
|
|
1165
750
|
// Base payload structure common to all channels
|
|
1166
751
|
const basePayload = {
|
|
1167
752
|
ouId: -1,
|
|
@@ -1242,12 +827,12 @@ const CommonTestAndPreview = (props) => {
|
|
|
1242
827
|
},
|
|
1243
828
|
smsDeliverySettings: {
|
|
1244
829
|
channelSettings: {
|
|
1245
|
-
channel: CHANNELS.SMS,
|
|
1246
830
|
gsmSenderId: deliverySettingsOverride?.gsmSenderId ?? '',
|
|
1247
831
|
domainId: deliverySettingsOverride?.domainId ?? null,
|
|
1248
832
|
domainGatewayMapId: deliverySettingsOverride?.domainGatewayMapId ?? '',
|
|
1249
833
|
targetNdnc: false,
|
|
1250
834
|
cdmaSenderId: deliverySettingsOverride?.cdmaSenderId ?? '',
|
|
835
|
+
channel: CHANNELS.SMS,
|
|
1251
836
|
},
|
|
1252
837
|
additionalSettings: {
|
|
1253
838
|
useTinyUrl: false,
|
|
@@ -1391,7 +976,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
1391
976
|
return {
|
|
1392
977
|
...basePayload,
|
|
1393
978
|
whatsappMessageContent: {
|
|
1394
|
-
messageBody:
|
|
979
|
+
messageBody: templateEditorValue || '',
|
|
1395
980
|
accountId: formDataObj?.accountId || '',
|
|
1396
981
|
sourceAccountIdentifier: sourceAccountIdentifier || formDataObj?.sourceAccountIdentifier || '',
|
|
1397
982
|
accountName: formDataObj?.accountName || '',
|
|
@@ -1416,7 +1001,16 @@ const CommonTestAndPreview = (props) => {
|
|
|
1416
1001
|
}
|
|
1417
1002
|
|
|
1418
1003
|
case CHANNELS.RCS:
|
|
1419
|
-
return
|
|
1004
|
+
return {
|
|
1005
|
+
...basePayload,
|
|
1006
|
+
rcsMessageContent: {
|
|
1007
|
+
channel: CHANNELS.RCS,
|
|
1008
|
+
messageBody: contentStr,
|
|
1009
|
+
rcsType: additionalProps?.rcsType,
|
|
1010
|
+
rcsImageSrc: formDataObj?.rcsImageSrc,
|
|
1011
|
+
rcsSuggestions: formDataObj?.rcsSuggestions,
|
|
1012
|
+
},
|
|
1013
|
+
};
|
|
1420
1014
|
|
|
1421
1015
|
case CHANNELS.INAPP: {
|
|
1422
1016
|
// InApp payload structure similar to MobilePush
|
|
@@ -2518,10 +2112,6 @@ const CommonTestAndPreview = (props) => {
|
|
|
2518
2112
|
formatMessage,
|
|
2519
2113
|
lastModified: formData?.lastModified,
|
|
2520
2114
|
updatedByName: formData?.updatedByName,
|
|
2521
|
-
smsFallbackContent: isRcsSmsFallbackPreviewEnabled ? smsFallbackContent : null,
|
|
2522
|
-
smsFallbackResolvedText,
|
|
2523
|
-
activePreviewTab,
|
|
2524
|
-
onPreviewTabChange: setActivePreviewTab,
|
|
2525
2115
|
};
|
|
2526
2116
|
};
|
|
2527
2117
|
|
|
@@ -2561,12 +2151,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
2561
2151
|
}, [show, beeInstance, currentTab, channel]);
|
|
2562
2152
|
|
|
2563
2153
|
/**
|
|
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.
|
|
2154
|
+
* Initial data load when slidebox opens
|
|
2570
2155
|
*/
|
|
2571
2156
|
useEffect(() => {
|
|
2572
2157
|
if (show) {
|
|
@@ -2651,61 +2236,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
2651
2236
|
actions.getTestGroupsRequested();
|
|
2652
2237
|
}
|
|
2653
2238
|
}
|
|
2654
|
-
|
|
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]);
|
|
2239
|
+
}, [show, beeInstance, currentTab, channel]);
|
|
2709
2240
|
|
|
2710
2241
|
/**
|
|
2711
2242
|
* Email-specific: Handle content updates for both BEE and CKEditor
|
|
@@ -2757,22 +2288,15 @@ const CommonTestAndPreview = (props) => {
|
|
|
2757
2288
|
setSelectedCustomer(null);
|
|
2758
2289
|
setRequiredTags([]);
|
|
2759
2290
|
setOptionalTags([]);
|
|
2760
|
-
setSmsFallbackExtractedTags([]);
|
|
2761
|
-
setSmsFallbackRequiredTags([]);
|
|
2762
|
-
setSmsFallbackOptionalTags([]);
|
|
2763
|
-
setIsExtractingSmsFallbackTags(false);
|
|
2764
2291
|
setCustomValues({});
|
|
2765
2292
|
setShowJSON(false);
|
|
2766
2293
|
setTagsExtracted(false);
|
|
2767
2294
|
setPreviewDevice(DESKTOP);
|
|
2768
|
-
setActivePreviewTab(PREVIEW_TAB_RCS);
|
|
2769
|
-
setSmsFallbackPreviewText(undefined);
|
|
2770
2295
|
setSelectedTestEntities([]);
|
|
2771
2296
|
actions.clearPrefilledValues();
|
|
2772
2297
|
} else {
|
|
2773
2298
|
// Reset device to initialDevice when opening (Android for mobile channels, Desktop for others)
|
|
2774
2299
|
setPreviewDevice(initialDevice);
|
|
2775
|
-
setActivePreviewTab(PREVIEW_TAB_RCS);
|
|
2776
2300
|
}
|
|
2777
2301
|
}, [show, initialDevice]);
|
|
2778
2302
|
|
|
@@ -2781,10 +2305,79 @@ const CommonTestAndPreview = (props) => {
|
|
|
2781
2305
|
*/
|
|
2782
2306
|
useEffect(() => {
|
|
2783
2307
|
if (previewData) {
|
|
2784
|
-
setPreviewDataHtml(
|
|
2308
|
+
setPreviewDataHtml(previewData);
|
|
2785
2309
|
}
|
|
2786
2310
|
}, [previewData]);
|
|
2787
2311
|
|
|
2312
|
+
/**
|
|
2313
|
+
* Process extracted tags and categorize them
|
|
2314
|
+
*/
|
|
2315
|
+
useEffect(() => {
|
|
2316
|
+
// Categorize tags into required and optional
|
|
2317
|
+
const required = [];
|
|
2318
|
+
const optional = [];
|
|
2319
|
+
let hasPersonalizationTags = false;
|
|
2320
|
+
|
|
2321
|
+
if (extractedTags?.length > 0) {
|
|
2322
|
+
const processTag = (tag, parentPath = '') => {
|
|
2323
|
+
const currentPath = parentPath ? `${parentPath}.${tag.name}` : tag.name;
|
|
2324
|
+
|
|
2325
|
+
// Skip unsubscribe tag for input fields
|
|
2326
|
+
if (tag?.name === UNSUBSCRIBE_TAG_NAME) {
|
|
2327
|
+
return;
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
hasPersonalizationTags = true;
|
|
2331
|
+
|
|
2332
|
+
if (tag?.metaData?.userDriven === false) {
|
|
2333
|
+
required.push({
|
|
2334
|
+
...tag,
|
|
2335
|
+
fullPath: currentPath,
|
|
2336
|
+
});
|
|
2337
|
+
} else if (tag?.metaData?.userDriven === true) {
|
|
2338
|
+
optional.push({
|
|
2339
|
+
...tag,
|
|
2340
|
+
fullPath: currentPath,
|
|
2341
|
+
});
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2344
|
+
if (tag?.children?.length > 0) {
|
|
2345
|
+
tag.children.forEach((child) => processTag(child, currentPath));
|
|
2346
|
+
}
|
|
2347
|
+
};
|
|
2348
|
+
|
|
2349
|
+
extractedTags.forEach((tag) => processTag(tag));
|
|
2350
|
+
|
|
2351
|
+
if (hasPersonalizationTags) {
|
|
2352
|
+
setRequiredTags(required);
|
|
2353
|
+
setOptionalTags(optional);
|
|
2354
|
+
setTagsExtracted(true); // Mark tags as extracted and processed
|
|
2355
|
+
|
|
2356
|
+
// Initialize custom values for required tags
|
|
2357
|
+
const initialValues = {};
|
|
2358
|
+
required.forEach((tag) => {
|
|
2359
|
+
initialValues[tag?.fullPath] = '';
|
|
2360
|
+
});
|
|
2361
|
+
optional.forEach((tag) => {
|
|
2362
|
+
initialValues[tag?.fullPath] = '';
|
|
2363
|
+
});
|
|
2364
|
+
setCustomValues(initialValues);
|
|
2365
|
+
} else {
|
|
2366
|
+
// Reset all tag-related state if no personalization tags
|
|
2367
|
+
setRequiredTags([]);
|
|
2368
|
+
setOptionalTags([]);
|
|
2369
|
+
setCustomValues({});
|
|
2370
|
+
setTagsExtracted(false);
|
|
2371
|
+
}
|
|
2372
|
+
} else {
|
|
2373
|
+
// Reset all tag-related state if no tags
|
|
2374
|
+
setRequiredTags([]);
|
|
2375
|
+
setOptionalTags([]);
|
|
2376
|
+
setCustomValues({});
|
|
2377
|
+
setTagsExtracted(false);
|
|
2378
|
+
}
|
|
2379
|
+
}, [extractedTags]);
|
|
2380
|
+
|
|
2788
2381
|
/**
|
|
2789
2382
|
* Handle customer selection and fetch prefilled values
|
|
2790
2383
|
*/
|
|
@@ -2792,15 +2385,17 @@ const CommonTestAndPreview = (props) => {
|
|
|
2792
2385
|
if (selectedCustomer && config.enableCustomerSearch !== false) {
|
|
2793
2386
|
setTagsExtracted(true); // Auto-open custom values editor
|
|
2794
2387
|
|
|
2388
|
+
// Get all available tags
|
|
2389
|
+
const allTags = [...requiredTags, ...optionalTags];
|
|
2795
2390
|
const requiredTagObj = {};
|
|
2796
|
-
|
|
2391
|
+
requiredTags.forEach((tag) => {
|
|
2797
2392
|
requiredTagObj[tag?.fullPath] = '';
|
|
2798
2393
|
});
|
|
2799
2394
|
if (allTags.length > 0) {
|
|
2800
2395
|
const payload = preparePreviewPayload(
|
|
2801
|
-
|
|
2396
|
+
channel,
|
|
2802
2397
|
formData || {},
|
|
2803
|
-
|
|
2398
|
+
getCurrentContent,
|
|
2804
2399
|
{
|
|
2805
2400
|
...requiredTagObj,
|
|
2806
2401
|
},
|
|
@@ -2809,7 +2404,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
2809
2404
|
actions.getPrefilledValuesRequested(payload);
|
|
2810
2405
|
}
|
|
2811
2406
|
}
|
|
2812
|
-
}, [selectedCustomer
|
|
2407
|
+
}, [selectedCustomer]);
|
|
2813
2408
|
|
|
2814
2409
|
/**
|
|
2815
2410
|
* Update custom values with prefilled values from API
|
|
@@ -2820,29 +2415,24 @@ const CommonTestAndPreview = (props) => {
|
|
|
2820
2415
|
if (prefilledValues && selectedCustomer) {
|
|
2821
2416
|
// Always replace all values with prefilled values
|
|
2822
2417
|
const updatedValues = {};
|
|
2823
|
-
|
|
2418
|
+
[...requiredTags, ...optionalTags].forEach((tag) => {
|
|
2824
2419
|
updatedValues[tag?.fullPath] = prefilledValues[tag?.fullPath] || '';
|
|
2825
2420
|
});
|
|
2826
2421
|
|
|
2827
2422
|
setCustomValues(updatedValues);
|
|
2828
2423
|
|
|
2829
2424
|
// 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;
|
|
2834
2425
|
const payload = preparePreviewPayload(
|
|
2835
|
-
|
|
2426
|
+
channel,
|
|
2836
2427
|
formData || {},
|
|
2837
|
-
|
|
2428
|
+
getCurrentContent,
|
|
2838
2429
|
updatedValues,
|
|
2839
2430
|
selectedCustomer
|
|
2840
2431
|
);
|
|
2841
2432
|
actions.updatePreviewRequested(payload);
|
|
2842
2433
|
setHasPreviewCallBeenMade(true); // Mark that preview call was made
|
|
2843
|
-
void syncSmsFallbackPreview(updatedValues, selectedCustomer);
|
|
2844
2434
|
}
|
|
2845
|
-
}, [JSON.stringify(prefilledValues), selectedCustomer
|
|
2435
|
+
}, [JSON.stringify(prefilledValues), selectedCustomer]);
|
|
2846
2436
|
|
|
2847
2437
|
/**
|
|
2848
2438
|
* Map channel constants to display names (lowercase for message)
|
|
@@ -2936,7 +2526,11 @@ const CommonTestAndPreview = (props) => {
|
|
|
2936
2526
|
setTagsExtracted(true); // Auto-open custom values editor
|
|
2937
2527
|
|
|
2938
2528
|
// Clear any existing values while waiting for prefilled values
|
|
2939
|
-
|
|
2529
|
+
const emptyValues = {};
|
|
2530
|
+
[...requiredTags, ...optionalTags].forEach((tag) => {
|
|
2531
|
+
emptyValues[tag?.fullPath] = '';
|
|
2532
|
+
});
|
|
2533
|
+
setCustomValues(emptyValues);
|
|
2940
2534
|
};
|
|
2941
2535
|
|
|
2942
2536
|
/**
|
|
@@ -2950,7 +2544,11 @@ const CommonTestAndPreview = (props) => {
|
|
|
2950
2544
|
actions.clearPreviewErrors();
|
|
2951
2545
|
|
|
2952
2546
|
// Initialize empty values for all tags
|
|
2953
|
-
|
|
2547
|
+
const emptyValues = {};
|
|
2548
|
+
[...requiredTags, ...optionalTags].forEach((tag) => {
|
|
2549
|
+
emptyValues[tag?.fullPath] = '';
|
|
2550
|
+
});
|
|
2551
|
+
setCustomValues(emptyValues);
|
|
2954
2552
|
|
|
2955
2553
|
// Don't make preview call when clearing selection - just reset to raw content
|
|
2956
2554
|
// Preview will be shown using raw formData/content
|
|
@@ -2986,20 +2584,17 @@ const CommonTestAndPreview = (props) => {
|
|
|
2986
2584
|
*/
|
|
2987
2585
|
const handleDiscardCustomValues = () => {
|
|
2988
2586
|
// Initialize empty values for all tags
|
|
2989
|
-
const emptyValues =
|
|
2587
|
+
const emptyValues = {};
|
|
2588
|
+
[...requiredTags, ...optionalTags].forEach((tag) => {
|
|
2589
|
+
emptyValues[tag?.fullPath] = '';
|
|
2590
|
+
});
|
|
2990
2591
|
setCustomValues(emptyValues);
|
|
2991
2592
|
|
|
2992
|
-
// Reset SMS fallback preview so it shows raw template (with {{tags}} visible) after discard
|
|
2993
|
-
setSmsFallbackPreviewText(undefined);
|
|
2994
|
-
|
|
2995
2593
|
// 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;
|
|
2999
2594
|
const payload = preparePreviewPayload(
|
|
3000
|
-
|
|
2595
|
+
channel,
|
|
3001
2596
|
formData || {},
|
|
3002
|
-
|
|
2597
|
+
getCurrentContent,
|
|
3003
2598
|
emptyValues,
|
|
3004
2599
|
selectedCustomer
|
|
3005
2600
|
);
|
|
@@ -3013,21 +2608,14 @@ const CommonTestAndPreview = (props) => {
|
|
|
3013
2608
|
*/
|
|
3014
2609
|
const handleUpdatePreview = async () => {
|
|
3015
2610
|
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;
|
|
3021
2611
|
const payload = preparePreviewPayload(
|
|
3022
|
-
|
|
2612
|
+
channel,
|
|
3023
2613
|
formData || {},
|
|
3024
|
-
|
|
2614
|
+
getCurrentContent,
|
|
3025
2615
|
customValues,
|
|
3026
2616
|
selectedCustomer
|
|
3027
2617
|
);
|
|
3028
2618
|
await actions.updatePreviewRequested(payload);
|
|
3029
|
-
|
|
3030
|
-
await syncSmsFallbackPreview(customValues, selectedCustomer);
|
|
3031
2619
|
setHasPreviewCallBeenMade(true); // Mark that preview call was made
|
|
3032
2620
|
} catch (error) {
|
|
3033
2621
|
CapNotification.error({
|
|
@@ -3037,115 +2625,25 @@ const CommonTestAndPreview = (props) => {
|
|
|
3037
2625
|
};
|
|
3038
2626
|
|
|
3039
2627
|
/**
|
|
3040
|
-
*
|
|
3041
|
-
*/
|
|
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
|
-
};
|
|
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.
|
|
2628
|
+
* Handle extract tags
|
|
3097
2629
|
*/
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
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]);
|
|
2630
|
+
const handleExtractTags = () => {
|
|
2631
|
+
// Get content based on channel
|
|
2632
|
+
let contentToExtract = getCurrentContent;
|
|
3119
2633
|
|
|
3120
|
-
/**
|
|
3121
|
-
* Get content to run tag extraction on (channel-specific).
|
|
3122
|
-
*/
|
|
3123
|
-
const getContentForTagExtraction = () => {
|
|
3124
|
-
let contentToExtract = activeContentForActions;
|
|
3125
2634
|
if (channel === CHANNELS.EMAIL && formData) {
|
|
3126
2635
|
const currentTabData = formData[currentTab - 1];
|
|
3127
2636
|
const activeTab = currentTabData?.activeTab;
|
|
3128
2637
|
const templateContent = currentTabData?.[activeTab]?.['template-content'];
|
|
3129
2638
|
contentToExtract = templateContent || contentToExtract;
|
|
3130
2639
|
}
|
|
3131
|
-
return contentToExtract;
|
|
3132
|
-
};
|
|
3133
2640
|
|
|
3134
|
-
|
|
3135
|
-
* Handle extract tags
|
|
3136
|
-
*/
|
|
3137
|
-
const handleExtractTags = () => {
|
|
3138
|
-
if (channel === CHANNELS.RCS) {
|
|
3139
|
-
applyRcsSmsFallbackTagExtraction();
|
|
3140
|
-
return;
|
|
3141
|
-
}
|
|
3142
|
-
|
|
3143
|
-
const contentToExtract = getContentForTagExtraction();
|
|
2641
|
+
// Check for personalization tags (excluding unsubscribe)
|
|
3144
2642
|
const tags = contentToExtract.match(/{{[^}]+}}/g) || [];
|
|
3145
2643
|
const hasPersonalizationTags = tags.some((tag) => !tag.includes(UNSUBSCRIBE_TAG_NAME));
|
|
3146
|
-
const onlyUnsubscribe = !hasPersonalizationTags && tags.length === 1 && tags[0].includes(UNSUBSCRIBE_TAG_NAME);
|
|
3147
2644
|
|
|
3148
|
-
if (
|
|
2645
|
+
if (!hasPersonalizationTags && tags.length === 1 && tags[0].includes(UNSUBSCRIBE_TAG_NAME)) {
|
|
2646
|
+
// If only unsubscribe tag is present, show noTagsExtracted message
|
|
3149
2647
|
setTagsExtracted(false);
|
|
3150
2648
|
setRequiredTags([]);
|
|
3151
2649
|
setOptionalTags([]);
|
|
@@ -3153,9 +2651,10 @@ const CommonTestAndPreview = (props) => {
|
|
|
3153
2651
|
return;
|
|
3154
2652
|
}
|
|
3155
2653
|
|
|
2654
|
+
// Extract tags
|
|
3156
2655
|
setTagsExtracted(true);
|
|
3157
2656
|
const { templateSubject, templateContent } = prepareTagExtractionPayload(
|
|
3158
|
-
|
|
2657
|
+
channel,
|
|
3159
2658
|
formData || {},
|
|
3160
2659
|
contentToExtract
|
|
3161
2660
|
);
|
|
@@ -3217,7 +2716,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
3217
2716
|
if (existingTestCustomer) {
|
|
3218
2717
|
const entityId = existingTestCustomer.userId ?? existingTestCustomer.customerId;
|
|
3219
2718
|
if (entityId != null) {
|
|
3220
|
-
const id = normalizeTestEntityId(entityId);
|
|
2719
|
+
const id = 'customer:' + normalizeTestEntityId(entityId);
|
|
3221
2720
|
setSelectedTestEntities((prev) => (
|
|
3222
2721
|
prev.some((existing) => testEntityIdsEqual(existing, id)) ? prev : [...prev, id]
|
|
3223
2722
|
));
|
|
@@ -3254,7 +2753,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
3254
2753
|
(c) => String(c?.customerId) === customerIdFromLookup || String(c?.userId) === customerIdFromLookup
|
|
3255
2754
|
);
|
|
3256
2755
|
if (alreadyInTestListByCustomerId) {
|
|
3257
|
-
const id = normalizeTestEntityId(customerIdFromLookup);
|
|
2756
|
+
const id = 'customer:' + normalizeTestEntityId(customerIdFromLookup);
|
|
3258
2757
|
setSelectedTestEntities((prev) => (
|
|
3259
2758
|
prev.some((existing) => testEntityIdsEqual(existing, id)) ? prev : [...prev, id]
|
|
3260
2759
|
));
|
|
@@ -3291,21 +2790,26 @@ const CommonTestAndPreview = (props) => {
|
|
|
3291
2790
|
const handleSendTestMessage = () => {
|
|
3292
2791
|
const allUserIds = [];
|
|
3293
2792
|
selectedTestEntities.forEach((entityId) => {
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
2793
|
+
if (String(entityId).startsWith('group:')) {
|
|
2794
|
+
const rawId = String(entityId).slice('group:'.length);
|
|
2795
|
+
const group = testGroups.find((g) => testEntityIdsEqual(g.groupId, rawId));
|
|
2796
|
+
if (group) {
|
|
2797
|
+
allUserIds.push(...group.userIds);
|
|
2798
|
+
}
|
|
3297
2799
|
} else {
|
|
3298
|
-
|
|
2800
|
+
const rawId = String(entityId).startsWith('customer:')
|
|
2801
|
+
? String(entityId).slice('customer:'.length)
|
|
2802
|
+
: String(entityId);
|
|
2803
|
+
allUserIds.push(Number(rawId));
|
|
3299
2804
|
}
|
|
3300
2805
|
});
|
|
3301
2806
|
const uniqueUserIds = [...new Set(allUserIds)];
|
|
3302
2807
|
|
|
3303
|
-
const deliveryOverride = [CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP
|
|
2808
|
+
const deliveryOverride = [CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP].includes(channel)
|
|
3304
2809
|
? testPreviewDeliverySettings[channel]
|
|
3305
2810
|
: null;
|
|
3306
2811
|
|
|
3307
|
-
//
|
|
3308
|
-
// Do not use activeChannelForActions / activeContentForActions — those follow the RCS vs Fallback SMS *preview* tab.
|
|
2812
|
+
// Create initial payload based on channel
|
|
3309
2813
|
const initialPayload = prepareTestMessagePayload(
|
|
3310
2814
|
channel,
|
|
3311
2815
|
formData || content || {},
|
|
@@ -3313,8 +2817,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
3313
2817
|
customValues,
|
|
3314
2818
|
uniqueUserIds,
|
|
3315
2819
|
previewData,
|
|
3316
|
-
deliveryOverride
|
|
3317
|
-
{},
|
|
2820
|
+
deliveryOverride
|
|
3318
2821
|
);
|
|
3319
2822
|
|
|
3320
2823
|
actions.createMessageMetaRequested(
|
|
@@ -3347,10 +2850,11 @@ const CommonTestAndPreview = (props) => {
|
|
|
3347
2850
|
// ============================================
|
|
3348
2851
|
// RENDER HELPER FUNCTIONS
|
|
3349
2852
|
// ============================================
|
|
2853
|
+
|
|
3350
2854
|
const renderLeftPanelContent = () => (
|
|
3351
2855
|
<LeftPanelContent
|
|
3352
|
-
isExtractingTags={isExtractingTags
|
|
3353
|
-
extractedTags={
|
|
2856
|
+
isExtractingTags={isExtractingTags}
|
|
2857
|
+
extractedTags={extractedTags}
|
|
3354
2858
|
selectedCustomer={selectedCustomer}
|
|
3355
2859
|
handleCustomerSelect={handleCustomerSelect}
|
|
3356
2860
|
handleSearchCustomer={handleSearchCustomer}
|
|
@@ -3368,29 +2872,15 @@ const CommonTestAndPreview = (props) => {
|
|
|
3368
2872
|
|
|
3369
2873
|
const renderCustomValuesEditor = () => (
|
|
3370
2874
|
<CustomValuesEditor
|
|
3371
|
-
isExtractingTags={isExtractingTags
|
|
2875
|
+
isExtractingTags={isExtractingTags}
|
|
3372
2876
|
isUpdatePreviewDisabled={isUpdatePreviewDisabled}
|
|
3373
2877
|
showJSON={showJSON}
|
|
3374
2878
|
setShowJSON={setShowJSON}
|
|
3375
2879
|
customValues={customValues}
|
|
3376
2880
|
handleJSONTextChange={handleJSONTextChange}
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
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
|
-
]}
|
|
2881
|
+
extractedTags={extractedTags}
|
|
2882
|
+
requiredTags={requiredTags}
|
|
2883
|
+
optionalTags={optionalTags}
|
|
3394
2884
|
handleCustomValueChange={handleCustomValueChange}
|
|
3395
2885
|
handleDiscardCustomValues={handleDiscardCustomValues}
|
|
3396
2886
|
handleUpdatePreview={handleUpdatePreview}
|
|
@@ -3406,13 +2896,18 @@ const CommonTestAndPreview = (props) => {
|
|
|
3406
2896
|
}));
|
|
3407
2897
|
};
|
|
3408
2898
|
|
|
3409
|
-
/** Trim pasted emails (trailing CR/LF).
|
|
2899
|
+
/** Trim pasted emails (trailing CR/LF). SMS: strip non-digits so pasted formatted numbers match isValidMobile / API. */
|
|
3410
2900
|
const handleTestCustomersSearch = useCallback((value) => {
|
|
3411
2901
|
if (value == null || value === '') {
|
|
3412
2902
|
setSearchValue('');
|
|
3413
2903
|
return;
|
|
3414
2904
|
}
|
|
3415
|
-
|
|
2905
|
+
const raw = String(value).trim();
|
|
2906
|
+
if (channel === CHANNELS.SMS) {
|
|
2907
|
+
setSearchValue(formatPhoneNumber(raw));
|
|
2908
|
+
} else {
|
|
2909
|
+
setSearchValue(raw);
|
|
2910
|
+
}
|
|
3416
2911
|
}, [channel]);
|
|
3417
2912
|
|
|
3418
2913
|
const renderSendTestMessage = () => (
|
|
@@ -3430,13 +2925,12 @@ const CommonTestAndPreview = (props) => {
|
|
|
3430
2925
|
renderAddTestCustomerButton={renderAddTestCustomerButton}
|
|
3431
2926
|
formatMessage={formatMessage}
|
|
3432
2927
|
deliverySettings={testPreviewDeliverySettings[channel]}
|
|
3433
|
-
|
|
2928
|
+
senderDetailsOptions={senderDetailsByChannel[channel]}
|
|
3434
2929
|
wecrmAccounts={wecrmAccounts}
|
|
3435
2930
|
onSaveDeliverySettings={handleSaveDeliverySettings}
|
|
3436
2931
|
isLoadingSenderDetails={isLoadingSenderDetails}
|
|
3437
2932
|
smsTraiDltEnabled={smsTraiDltEnabled}
|
|
3438
2933
|
registeredSenderIds={registeredSenderIds}
|
|
3439
|
-
isChannelSmsFallbackPreviewEnabled={isRcsSmsFallbackPreviewEnabled}
|
|
3440
2934
|
searchValue={searchValue}
|
|
3441
2935
|
setSearchValue={handleTestCustomersSearch}
|
|
3442
2936
|
/>
|
|
@@ -3450,13 +2944,14 @@ const CommonTestAndPreview = (props) => {
|
|
|
3450
2944
|
|
|
3451
2945
|
const renderAddTestCustomerButton = () => {
|
|
3452
2946
|
const raw = (searchValue || '').trim();
|
|
2947
|
+
const value = channel === CHANNELS.SMS ? formatPhoneNumber(raw) : raw;
|
|
3453
2948
|
const showAddButton =
|
|
3454
2949
|
[CHANNELS.EMAIL, CHANNELS.SMS].includes(channel) &&
|
|
3455
|
-
(channel === CHANNELS.EMAIL ? isValidEmail(
|
|
2950
|
+
(channel === CHANNELS.EMAIL ? isValidEmail(value) : isValidMobile(value));
|
|
3456
2951
|
if (!showAddButton) return null;
|
|
3457
2952
|
return (
|
|
3458
2953
|
<AddTestCustomerButton
|
|
3459
|
-
searchValue={
|
|
2954
|
+
searchValue={value}
|
|
3460
2955
|
handleAddTestCustomer={handleAddTestCustomer}
|
|
3461
2956
|
/>
|
|
3462
2957
|
);
|