@capillarytech/creatives-library 8.0.345-alpha.15 → 8.0.345-alpha.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/package.json +1 -1
- package/services/tests/api.test.js +0 -13
- package/utils/commonUtils.js +1 -19
- package/v2Components/CapActionButton/constants.js +0 -7
- package/v2Components/CapActionButton/index.js +109 -167
- 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 -70
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -207
- 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 -160
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +76 -341
- 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 -676
- package/v2Components/CommonTestAndPreview/messages.js +3 -49
- package/v2Components/CommonTestAndPreview/sagas.js +6 -15
- 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 +2 -2
- package/v2Components/FormBuilder/index.js +10 -8
- package/v2Components/TemplatePreview/_templatePreview.scss +23 -33
- package/v2Components/TemplatePreview/index.js +28 -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 +103 -300
- 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/Email/reducer.js +12 -3
- package/v2Containers/Email/sagas.js +9 -4
- package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +4 -0
- package/v2Containers/Email/tests/reducer.test.js +47 -0
- package/v2Containers/Email/tests/sagas.test.js +146 -6
- package/v2Containers/Rcs/constants.js +8 -119
- package/v2Containers/Rcs/index.js +811 -2383
- package/v2Containers/Rcs/index.scss +6 -276
- package/v2Containers/Rcs/messages.js +3 -38
- 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 -100
- package/v2Containers/SmsTrai/Create/index.js +4 -9
- package/v2Containers/SmsTrai/Edit/constants.js +0 -2
- package/v2Containers/SmsTrai/Edit/index.js +130 -636
- 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 +2 -163
- package/v2Containers/Templates/actions.js +0 -11
- package/v2Containers/Templates/constants.js +0 -2
- package/v2Containers/Templates/index.js +54 -119
- package/v2Containers/Templates/sagas.js +12 -57
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1079 -1043
- package/v2Containers/Templates/tests/sagas.test.js +123 -193
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -72
- package/v2Containers/TemplatesV2/index.js +23 -86
- 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/templateVarUtils.test.js +0 -204
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js.rej +0 -18
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -87
- package/v2Components/SmsFallback/constants.js +0 -73
- package/v2Components/SmsFallback/index.js +0 -955
- package/v2Components/SmsFallback/index.scss +0 -265
- package/v2Components/SmsFallback/messages.js +0 -78
- package/v2Components/SmsFallback/smsFallbackUtils.js +0 -118
- 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 -197
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -277
- 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 -67
- 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/index.js.rej +0 -1336
- package/v2Containers/Rcs/index.scss.rej +0 -74
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -225
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap.rej +0 -128
- 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,130 +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
|
-
const smsFallbackTaggedTemplateBody = pickFirstSmsFallbackTemplateString(
|
|
1082
|
-
smsFallbackFromCreativeForm,
|
|
1083
|
-
) || '';
|
|
1084
|
-
const smsSenderFromDelivery = deliverySettingsOverride?.cdmaSenderId?.includes('|')
|
|
1085
|
-
? deliverySettingsOverride.cdmaSenderId.split('|')[1]
|
|
1086
|
-
: deliverySettingsOverride?.cdmaSenderId;
|
|
1087
|
-
const deliveryFallbackSmsId =
|
|
1088
|
-
typeof smsSenderFromDelivery === 'string' ? smsSenderFromDelivery.trim() : '';
|
|
1089
|
-
const creativeFallbackSmsId =
|
|
1090
|
-
smsFallbackFromCreativeForm?.senderId != null
|
|
1091
|
-
? String(smsFallbackFromCreativeForm.senderId).trim()
|
|
1092
|
-
: '';
|
|
1093
|
-
const fallbackSmsSenderIdForChannel = deliveryFallbackSmsId || creativeFallbackSmsId || '';
|
|
1094
|
-
|
|
1095
|
-
const smsFallBackContent =
|
|
1096
|
-
smsFallbackTaggedTemplateBody.trim() !== ''
|
|
1097
|
-
? { message: smsFallbackTaggedTemplateBody }
|
|
1098
|
-
: undefined;
|
|
1099
|
-
|
|
1100
|
-
// accountId: WeCRM account id (not sourceAccountIdentifier) for createMessageMeta
|
|
1101
|
-
const accountIdForMeta =
|
|
1102
|
-
rcsContentFromForm?.accountId != null && String(rcsContentFromForm.accountId).trim() !== ''
|
|
1103
|
-
? String(rcsContentFromForm.accountId)
|
|
1104
|
-
: undefined;
|
|
1105
|
-
|
|
1106
|
-
const rcsRichCardContent = {
|
|
1107
|
-
contentType: RCS_TEST_META_CONTENT_TYPE_RICHCARD,
|
|
1108
|
-
cardType: rcsContentFromForm?.cardType ?? RCS_TEST_META_CARD_TYPE_STANDALONE,
|
|
1109
|
-
cardSettings: rcsContentFromForm?.cardSettings ?? {
|
|
1110
|
-
cardOrientation: RCS_TEST_META_CARD_ORIENTATION_VERTICAL,
|
|
1111
|
-
cardWidth: RCS_TEST_META_CARD_WIDTH_SMALL,
|
|
1112
|
-
},
|
|
1113
|
-
...(cardContentForTestMetaApi.length > 0 && { cardContent: cardContentForTestMetaApi }),
|
|
1114
|
-
};
|
|
1115
|
-
|
|
1116
|
-
const rcsMessageContent = {
|
|
1117
|
-
channel: CHANNELS.RCS,
|
|
1118
|
-
...(accountIdForMeta && { accountId: accountIdForMeta }),
|
|
1119
|
-
rcsRichCardContent,
|
|
1120
|
-
...(smsFallBackContent && { smsFallBackContent }),
|
|
1121
|
-
};
|
|
1122
|
-
const rcsComposite = deliverySettingsOverride?.gsmSenderId ?? '';
|
|
1123
|
-
const [rcsDomainId, rcsSenderId] = rcsComposite.includes('|') ? rcsComposite.split('|') : ['', rcsComposite];
|
|
1124
|
-
const rcsDeliverySettings = {
|
|
1125
|
-
channelSettings: {
|
|
1126
|
-
channel: CHANNELS.RCS,
|
|
1127
|
-
rcsSender: (rcsSenderId || deliverySettingsOverride?.rcsSender) ?? '',
|
|
1128
|
-
domainId:
|
|
1129
|
-
rcsDomainId !== '' && rcsDomainId !== undefined && !Number.isNaN(Number(rcsDomainId))
|
|
1130
|
-
? Number(rcsDomainId)
|
|
1131
|
-
: (deliverySettingsOverride?.domainId ?? 0),
|
|
1132
|
-
fallbackSmsSenderId: fallbackSmsSenderIdForChannel,
|
|
1133
|
-
},
|
|
1134
|
-
additionalSettings: {
|
|
1135
|
-
useTinyUrl: false,
|
|
1136
|
-
encryptUrl: false,
|
|
1137
|
-
linkTrackingEnabled: false,
|
|
1138
|
-
bypassControlUser: false,
|
|
1139
|
-
userSubscriptionDisabled: false,
|
|
1140
|
-
},
|
|
1141
|
-
};
|
|
1142
|
-
const { clientName: baseClientName = CLIENT_NAME_CREATIVES, ...restBase } = basePayload;
|
|
1143
|
-
return {
|
|
1144
|
-
...restBase,
|
|
1145
|
-
rcsMessageContent,
|
|
1146
|
-
rcsDeliverySettings,
|
|
1147
|
-
executionParams: {},
|
|
1148
|
-
clientName: baseClientName,
|
|
1149
|
-
};
|
|
1150
|
-
};
|
|
1151
|
-
|
|
1152
|
-
const prepareTestMessagePayload = (
|
|
1153
|
-
channelType,
|
|
1154
|
-
formDataObj,
|
|
1155
|
-
contentStr,
|
|
1156
|
-
customValuesObj,
|
|
1157
|
-
recipientDetails,
|
|
1158
|
-
previewDataObj,
|
|
1159
|
-
deliverySettingsOverride,
|
|
1160
|
-
rcsExtra = {},
|
|
1161
|
-
) => {
|
|
749
|
+
const prepareTestMessagePayload = (channelType, formDataObj, contentStr, customValuesObj, recipientDetails, previewDataObj, deliverySettingsOverride) => {
|
|
1162
750
|
// Base payload structure common to all channels
|
|
1163
751
|
const basePayload = {
|
|
1164
752
|
ouId: -1,
|
|
@@ -1239,12 +827,12 @@ const CommonTestAndPreview = (props) => {
|
|
|
1239
827
|
},
|
|
1240
828
|
smsDeliverySettings: {
|
|
1241
829
|
channelSettings: {
|
|
1242
|
-
channel: CHANNELS.SMS,
|
|
1243
830
|
gsmSenderId: deliverySettingsOverride?.gsmSenderId ?? '',
|
|
1244
831
|
domainId: deliverySettingsOverride?.domainId ?? null,
|
|
1245
832
|
domainGatewayMapId: deliverySettingsOverride?.domainGatewayMapId ?? '',
|
|
1246
833
|
targetNdnc: false,
|
|
1247
834
|
cdmaSenderId: deliverySettingsOverride?.cdmaSenderId ?? '',
|
|
835
|
+
channel: CHANNELS.SMS,
|
|
1248
836
|
},
|
|
1249
837
|
additionalSettings: {
|
|
1250
838
|
useTinyUrl: false,
|
|
@@ -1388,7 +976,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
1388
976
|
return {
|
|
1389
977
|
...basePayload,
|
|
1390
978
|
whatsappMessageContent: {
|
|
1391
|
-
messageBody:
|
|
979
|
+
messageBody: templateEditorValue || '',
|
|
1392
980
|
accountId: formDataObj?.accountId || '',
|
|
1393
981
|
sourceAccountIdentifier: sourceAccountIdentifier || formDataObj?.sourceAccountIdentifier || '',
|
|
1394
982
|
accountName: formDataObj?.accountName || '',
|
|
@@ -1413,7 +1001,16 @@ const CommonTestAndPreview = (props) => {
|
|
|
1413
1001
|
}
|
|
1414
1002
|
|
|
1415
1003
|
case CHANNELS.RCS:
|
|
1416
|
-
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
|
+
};
|
|
1417
1014
|
|
|
1418
1015
|
case CHANNELS.INAPP: {
|
|
1419
1016
|
// InApp payload structure similar to MobilePush
|
|
@@ -2515,10 +2112,6 @@ const CommonTestAndPreview = (props) => {
|
|
|
2515
2112
|
formatMessage,
|
|
2516
2113
|
lastModified: formData?.lastModified,
|
|
2517
2114
|
updatedByName: formData?.updatedByName,
|
|
2518
|
-
smsFallbackContent: isRcsSmsFallbackPreviewEnabled ? smsFallbackContent : null,
|
|
2519
|
-
smsFallbackResolvedText,
|
|
2520
|
-
activePreviewTab,
|
|
2521
|
-
onPreviewTabChange: setActivePreviewTab,
|
|
2522
2115
|
};
|
|
2523
2116
|
};
|
|
2524
2117
|
|
|
@@ -2558,12 +2151,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
2558
2151
|
}, [show, beeInstance, currentTab, channel]);
|
|
2559
2152
|
|
|
2560
2153
|
/**
|
|
2561
|
-
* Initial data load when slidebox opens
|
|
2562
|
-
* EXTRACT TAGS CALL SITES (on open/edit RCS):
|
|
2563
|
-
* 1. Here (non-email): actions.extractTagsRequested() at line ~2161 when show is true.
|
|
2564
|
-
* 2. RCS SMS fallback useEffect below: Api.extractTagsWithMetaData() for fallback message.
|
|
2565
|
-
* 3. handleExtractTags() (user clicks "Enter custom values for tags") – not from effects.
|
|
2566
|
-
* The "Process extracted tags" effect only processes API results and must not call extract again.
|
|
2154
|
+
* Initial data load when slidebox opens
|
|
2567
2155
|
*/
|
|
2568
2156
|
useEffect(() => {
|
|
2569
2157
|
if (show) {
|
|
@@ -2648,61 +2236,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
2648
2236
|
actions.getTestGroupsRequested();
|
|
2649
2237
|
}
|
|
2650
2238
|
}
|
|
2651
|
-
|
|
2652
|
-
}, [show, beeInstance, currentTab, channel, getCurrentContent]);
|
|
2653
|
-
|
|
2654
|
-
/**
|
|
2655
|
-
* RCS with SMS fallback: extract tags for fallback SMS content as well
|
|
2656
|
-
* (so we can show a separate "Fallback SMS tags" section in left panel).
|
|
2657
|
-
*/
|
|
2658
|
-
useEffect(() => {
|
|
2659
|
-
let cancelled = false;
|
|
2660
|
-
|
|
2661
|
-
if (!show || channel !== CHANNELS.RCS) {
|
|
2662
|
-
return () => {
|
|
2663
|
-
cancelled = true;
|
|
2664
|
-
};
|
|
2665
|
-
}
|
|
2666
|
-
|
|
2667
|
-
if (!smsFallbackContent?.templateContent && !smsFallbackContent?.content) {
|
|
2668
|
-
setSmsFallbackExtractedTags([]);
|
|
2669
|
-
setSmsFallbackRequiredTags([]);
|
|
2670
|
-
setSmsFallbackOptionalTags([]);
|
|
2671
|
-
return () => {
|
|
2672
|
-
cancelled = true;
|
|
2673
|
-
};
|
|
2674
|
-
}
|
|
2675
|
-
|
|
2676
|
-
setIsExtractingSmsFallbackTags(true);
|
|
2677
|
-
(async () => {
|
|
2678
|
-
try {
|
|
2679
|
-
const fallbackBodyForExtractApi = getSmsFallbackTextForTagExtraction(smsFallbackContent);
|
|
2680
|
-
const payload = {
|
|
2681
|
-
messageTitle: '',
|
|
2682
|
-
messageBody: fallbackBodyForExtractApi || '',
|
|
2683
|
-
};
|
|
2684
|
-
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
|
|
2685
|
-
let smsFallbackTagTree = response?.data ?? [];
|
|
2686
|
-
if (!Array.isArray(smsFallbackTagTree)) smsFallbackTagTree = [];
|
|
2687
|
-
if (!smsTemplateHasMustacheTags(fallbackBodyForExtractApi)) {
|
|
2688
|
-
smsFallbackTagTree = [];
|
|
2689
|
-
} else if (smsFallbackTagTree.length === 0) {
|
|
2690
|
-
smsFallbackTagTree = buildSyntheticSmsMustacheTags(fallbackBodyForExtractApi);
|
|
2691
|
-
}
|
|
2692
|
-
if (cancelled) return;
|
|
2693
|
-
setSmsFallbackExtractedTags(smsFallbackTagTree);
|
|
2694
|
-
} catch (e) {
|
|
2695
|
-
if (cancelled) return;
|
|
2696
|
-
setSmsFallbackExtractedTags([]);
|
|
2697
|
-
} finally {
|
|
2698
|
-
if (!cancelled) setIsExtractingSmsFallbackTags(false);
|
|
2699
|
-
}
|
|
2700
|
-
})();
|
|
2701
|
-
|
|
2702
|
-
return () => {
|
|
2703
|
-
cancelled = true;
|
|
2704
|
-
};
|
|
2705
|
-
}, [show, channel, smsFallbackContent]);
|
|
2239
|
+
}, [show, beeInstance, currentTab, channel]);
|
|
2706
2240
|
|
|
2707
2241
|
/**
|
|
2708
2242
|
* Email-specific: Handle content updates for both BEE and CKEditor
|
|
@@ -2754,22 +2288,15 @@ const CommonTestAndPreview = (props) => {
|
|
|
2754
2288
|
setSelectedCustomer(null);
|
|
2755
2289
|
setRequiredTags([]);
|
|
2756
2290
|
setOptionalTags([]);
|
|
2757
|
-
setSmsFallbackExtractedTags([]);
|
|
2758
|
-
setSmsFallbackRequiredTags([]);
|
|
2759
|
-
setSmsFallbackOptionalTags([]);
|
|
2760
|
-
setIsExtractingSmsFallbackTags(false);
|
|
2761
2291
|
setCustomValues({});
|
|
2762
2292
|
setShowJSON(false);
|
|
2763
2293
|
setTagsExtracted(false);
|
|
2764
2294
|
setPreviewDevice(DESKTOP);
|
|
2765
|
-
setActivePreviewTab(PREVIEW_TAB_RCS);
|
|
2766
|
-
setSmsFallbackPreviewText(undefined);
|
|
2767
2295
|
setSelectedTestEntities([]);
|
|
2768
2296
|
actions.clearPrefilledValues();
|
|
2769
2297
|
} else {
|
|
2770
2298
|
// Reset device to initialDevice when opening (Android for mobile channels, Desktop for others)
|
|
2771
2299
|
setPreviewDevice(initialDevice);
|
|
2772
|
-
setActivePreviewTab(PREVIEW_TAB_RCS);
|
|
2773
2300
|
}
|
|
2774
2301
|
}, [show, initialDevice]);
|
|
2775
2302
|
|
|
@@ -2778,10 +2305,79 @@ const CommonTestAndPreview = (props) => {
|
|
|
2778
2305
|
*/
|
|
2779
2306
|
useEffect(() => {
|
|
2780
2307
|
if (previewData) {
|
|
2781
|
-
setPreviewDataHtml(
|
|
2308
|
+
setPreviewDataHtml(previewData);
|
|
2782
2309
|
}
|
|
2783
2310
|
}, [previewData]);
|
|
2784
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
|
+
|
|
2785
2381
|
/**
|
|
2786
2382
|
* Handle customer selection and fetch prefilled values
|
|
2787
2383
|
*/
|
|
@@ -2789,15 +2385,17 @@ const CommonTestAndPreview = (props) => {
|
|
|
2789
2385
|
if (selectedCustomer && config.enableCustomerSearch !== false) {
|
|
2790
2386
|
setTagsExtracted(true); // Auto-open custom values editor
|
|
2791
2387
|
|
|
2388
|
+
// Get all available tags
|
|
2389
|
+
const allTags = [...requiredTags, ...optionalTags];
|
|
2792
2390
|
const requiredTagObj = {};
|
|
2793
|
-
|
|
2391
|
+
requiredTags.forEach((tag) => {
|
|
2794
2392
|
requiredTagObj[tag?.fullPath] = '';
|
|
2795
2393
|
});
|
|
2796
2394
|
if (allTags.length > 0) {
|
|
2797
2395
|
const payload = preparePreviewPayload(
|
|
2798
|
-
|
|
2396
|
+
channel,
|
|
2799
2397
|
formData || {},
|
|
2800
|
-
|
|
2398
|
+
getCurrentContent,
|
|
2801
2399
|
{
|
|
2802
2400
|
...requiredTagObj,
|
|
2803
2401
|
},
|
|
@@ -2806,7 +2404,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
2806
2404
|
actions.getPrefilledValuesRequested(payload);
|
|
2807
2405
|
}
|
|
2808
2406
|
}
|
|
2809
|
-
}, [selectedCustomer
|
|
2407
|
+
}, [selectedCustomer]);
|
|
2810
2408
|
|
|
2811
2409
|
/**
|
|
2812
2410
|
* Update custom values with prefilled values from API
|
|
@@ -2817,7 +2415,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
2817
2415
|
if (prefilledValues && selectedCustomer) {
|
|
2818
2416
|
// Always replace all values with prefilled values
|
|
2819
2417
|
const updatedValues = {};
|
|
2820
|
-
|
|
2418
|
+
[...requiredTags, ...optionalTags].forEach((tag) => {
|
|
2821
2419
|
updatedValues[tag?.fullPath] = prefilledValues[tag?.fullPath] || '';
|
|
2822
2420
|
});
|
|
2823
2421
|
|
|
@@ -2825,17 +2423,16 @@ const CommonTestAndPreview = (props) => {
|
|
|
2825
2423
|
|
|
2826
2424
|
// Update preview with prefilled values (this is a valid preview call trigger)
|
|
2827
2425
|
const payload = preparePreviewPayload(
|
|
2828
|
-
|
|
2426
|
+
channel,
|
|
2829
2427
|
formData || {},
|
|
2830
|
-
|
|
2428
|
+
getCurrentContent,
|
|
2831
2429
|
updatedValues,
|
|
2832
2430
|
selectedCustomer
|
|
2833
2431
|
);
|
|
2834
2432
|
actions.updatePreviewRequested(payload);
|
|
2835
2433
|
setHasPreviewCallBeenMade(true); // Mark that preview call was made
|
|
2836
|
-
void syncSmsFallbackPreview(updatedValues, selectedCustomer);
|
|
2837
2434
|
}
|
|
2838
|
-
}, [JSON.stringify(prefilledValues), selectedCustomer
|
|
2435
|
+
}, [JSON.stringify(prefilledValues), selectedCustomer]);
|
|
2839
2436
|
|
|
2840
2437
|
/**
|
|
2841
2438
|
* Map channel constants to display names (lowercase for message)
|
|
@@ -2929,7 +2526,11 @@ const CommonTestAndPreview = (props) => {
|
|
|
2929
2526
|
setTagsExtracted(true); // Auto-open custom values editor
|
|
2930
2527
|
|
|
2931
2528
|
// Clear any existing values while waiting for prefilled values
|
|
2932
|
-
|
|
2529
|
+
const emptyValues = {};
|
|
2530
|
+
[...requiredTags, ...optionalTags].forEach((tag) => {
|
|
2531
|
+
emptyValues[tag?.fullPath] = '';
|
|
2532
|
+
});
|
|
2533
|
+
setCustomValues(emptyValues);
|
|
2933
2534
|
};
|
|
2934
2535
|
|
|
2935
2536
|
/**
|
|
@@ -2943,7 +2544,11 @@ const CommonTestAndPreview = (props) => {
|
|
|
2943
2544
|
actions.clearPreviewErrors();
|
|
2944
2545
|
|
|
2945
2546
|
// Initialize empty values for all tags
|
|
2946
|
-
|
|
2547
|
+
const emptyValues = {};
|
|
2548
|
+
[...requiredTags, ...optionalTags].forEach((tag) => {
|
|
2549
|
+
emptyValues[tag?.fullPath] = '';
|
|
2550
|
+
});
|
|
2551
|
+
setCustomValues(emptyValues);
|
|
2947
2552
|
|
|
2948
2553
|
// Don't make preview call when clearing selection - just reset to raw content
|
|
2949
2554
|
// Preview will be shown using raw formData/content
|
|
@@ -2979,17 +2584,17 @@ const CommonTestAndPreview = (props) => {
|
|
|
2979
2584
|
*/
|
|
2980
2585
|
const handleDiscardCustomValues = () => {
|
|
2981
2586
|
// Initialize empty values for all tags
|
|
2982
|
-
const emptyValues =
|
|
2587
|
+
const emptyValues = {};
|
|
2588
|
+
[...requiredTags, ...optionalTags].forEach((tag) => {
|
|
2589
|
+
emptyValues[tag?.fullPath] = '';
|
|
2590
|
+
});
|
|
2983
2591
|
setCustomValues(emptyValues);
|
|
2984
2592
|
|
|
2985
|
-
// Reset SMS fallback preview so it shows raw template (with {{tags}} visible) after discard
|
|
2986
|
-
setSmsFallbackPreviewText(undefined);
|
|
2987
|
-
|
|
2988
2593
|
// Update preview with empty values (this is a valid preview call trigger)
|
|
2989
2594
|
const payload = preparePreviewPayload(
|
|
2990
|
-
|
|
2595
|
+
channel,
|
|
2991
2596
|
formData || {},
|
|
2992
|
-
|
|
2597
|
+
getCurrentContent,
|
|
2993
2598
|
emptyValues,
|
|
2994
2599
|
selectedCustomer
|
|
2995
2600
|
);
|
|
@@ -3004,15 +2609,13 @@ const CommonTestAndPreview = (props) => {
|
|
|
3004
2609
|
const handleUpdatePreview = async () => {
|
|
3005
2610
|
try {
|
|
3006
2611
|
const payload = preparePreviewPayload(
|
|
3007
|
-
|
|
2612
|
+
channel,
|
|
3008
2613
|
formData || {},
|
|
3009
|
-
|
|
2614
|
+
getCurrentContent,
|
|
3010
2615
|
customValues,
|
|
3011
2616
|
selectedCustomer
|
|
3012
2617
|
);
|
|
3013
2618
|
await actions.updatePreviewRequested(payload);
|
|
3014
|
-
|
|
3015
|
-
await syncSmsFallbackPreview(customValues, selectedCustomer);
|
|
3016
2619
|
setHasPreviewCallBeenMade(true); // Mark that preview call was made
|
|
3017
2620
|
} catch (error) {
|
|
3018
2621
|
CapNotification.error({
|
|
@@ -3022,115 +2625,25 @@ const CommonTestAndPreview = (props) => {
|
|
|
3022
2625
|
};
|
|
3023
2626
|
|
|
3024
2627
|
/**
|
|
3025
|
-
*
|
|
3026
|
-
*/
|
|
3027
|
-
const categorizeTags = (tagsTree = []) => {
|
|
3028
|
-
const required = [];
|
|
3029
|
-
const optional = [];
|
|
3030
|
-
let hasPersonalizationTags = false;
|
|
3031
|
-
const processTag = (tag, parentPath = '') => {
|
|
3032
|
-
const currentPath = parentPath ? `${parentPath}.${tag.name}` : tag.name;
|
|
3033
|
-
|
|
3034
|
-
// Skip unsubscribe tag for input fields
|
|
3035
|
-
if (tag?.name === UNSUBSCRIBE_TAG_NAME) return;
|
|
3036
|
-
|
|
3037
|
-
hasPersonalizationTags = true;
|
|
3038
|
-
const userDriven = tag?.metaData?.userDriven;
|
|
3039
|
-
if (userDriven === true) {
|
|
3040
|
-
optional.push({ ...tag, fullPath: currentPath });
|
|
3041
|
-
} else {
|
|
3042
|
-
// false or missing (SMS/DLT extract often omits metaData) → required for test values
|
|
3043
|
-
required.push({ ...tag, fullPath: currentPath });
|
|
3044
|
-
}
|
|
3045
|
-
|
|
3046
|
-
if (tag?.children?.length > 0) {
|
|
3047
|
-
tag.children.forEach((child) => processTag(child, currentPath));
|
|
3048
|
-
}
|
|
3049
|
-
};
|
|
3050
|
-
|
|
3051
|
-
(tagsTree || []).forEach((tag) => processTag(tag));
|
|
3052
|
-
return { required, optional, hasPersonalizationTags };
|
|
3053
|
-
};
|
|
3054
|
-
|
|
3055
|
-
/**
|
|
3056
|
-
* Apply tag extraction when content comes from RCS + SMS fallback (no API call).
|
|
3057
|
-
*/
|
|
3058
|
-
const applyRcsSmsFallbackTagExtraction = () => {
|
|
3059
|
-
const rcsPrimaryCategorized = categorizeTags(extractedTags ?? []);
|
|
3060
|
-
const fallbackSmsResolvedForTags = smsFallbackTextForTagExtraction ?? '';
|
|
3061
|
-
let fallbackSmsTagTree = smsFallbackExtractedTags?.length > 0
|
|
3062
|
-
? smsFallbackExtractedTags
|
|
3063
|
-
: buildSyntheticSmsMustacheTags(fallbackSmsResolvedForTags);
|
|
3064
|
-
if (!smsTemplateHasMustacheTags(fallbackSmsResolvedForTags)) {
|
|
3065
|
-
fallbackSmsTagTree = [];
|
|
3066
|
-
}
|
|
3067
|
-
const fallbackSmsCategorized = categorizeTags(fallbackSmsTagTree);
|
|
3068
|
-
setRequiredTags(rcsPrimaryCategorized.required);
|
|
3069
|
-
setOptionalTags(rcsPrimaryCategorized.optional);
|
|
3070
|
-
setSmsFallbackRequiredTags(fallbackSmsCategorized.required);
|
|
3071
|
-
setSmsFallbackOptionalTags(fallbackSmsCategorized.optional);
|
|
3072
|
-
setTagsExtracted(
|
|
3073
|
-
rcsPrimaryCategorized.hasPersonalizationTags || fallbackSmsCategorized.hasPersonalizationTags,
|
|
3074
|
-
);
|
|
3075
|
-
setCustomValues((prev) => mergeCustomValuesWithTagKeys(prev, rcsPrimaryCategorized, fallbackSmsCategorized));
|
|
3076
|
-
};
|
|
3077
|
-
|
|
3078
|
-
/**
|
|
3079
|
-
* When extract-tags API returns, map Redux `extractedTags` into required/optional so
|
|
3080
|
-
* CustomValuesEditor shows personalization fields (effect was previously commented out).
|
|
3081
|
-
* RCS + SMS fallback: merge primary + fallback tag trees when fallback template exists.
|
|
2628
|
+
* Handle extract tags
|
|
3082
2629
|
*/
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
if (channel === CHANNELS.RCS && hasFallbackSmsTemplate) {
|
|
3087
|
-
applyRcsSmsFallbackTagExtraction();
|
|
3088
|
-
return;
|
|
3089
|
-
}
|
|
3090
|
-
const smsEditorBody = typeof getCurrentContent === 'string' ? getCurrentContent : '';
|
|
3091
|
-
let smsTagSource = channel === CHANNELS.SMS && (!extractedTags || extractedTags.length === 0)
|
|
3092
|
-
? buildSyntheticSmsMustacheTags(smsEditorBody)
|
|
3093
|
-
: (extractedTags ?? []);
|
|
3094
|
-
if (channel === CHANNELS.SMS && !smsTemplateHasMustacheTags(smsEditorBody)) {
|
|
3095
|
-
smsTagSource = [];
|
|
3096
|
-
}
|
|
3097
|
-
const { required, optional, hasPersonalizationTags } = categorizeTags(smsTagSource);
|
|
3098
|
-
setRequiredTags(required);
|
|
3099
|
-
setOptionalTags(optional);
|
|
3100
|
-
setTagsExtracted(hasPersonalizationTags);
|
|
3101
|
-
setCustomValues((prev) => mergeCustomValuesWithTagKeys(prev, { required, optional }, { required: [], optional: [] }));
|
|
3102
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps -- applyRcsSmsFallbackTagExtraction closes over latest extractedTags/smsFallbackExtractedTags
|
|
3103
|
-
}, [show, extractedTags, channel, smsFallbackContent, smsFallbackExtractedTags, getCurrentContent, smsFallbackTextForTagExtraction]);
|
|
2630
|
+
const handleExtractTags = () => {
|
|
2631
|
+
// Get content based on channel
|
|
2632
|
+
let contentToExtract = getCurrentContent;
|
|
3104
2633
|
|
|
3105
|
-
/**
|
|
3106
|
-
* Get content to run tag extraction on (channel-specific).
|
|
3107
|
-
*/
|
|
3108
|
-
const getContentForTagExtraction = () => {
|
|
3109
|
-
let contentToExtract = activeContentForActions;
|
|
3110
2634
|
if (channel === CHANNELS.EMAIL && formData) {
|
|
3111
2635
|
const currentTabData = formData[currentTab - 1];
|
|
3112
2636
|
const activeTab = currentTabData?.activeTab;
|
|
3113
2637
|
const templateContent = currentTabData?.[activeTab]?.['template-content'];
|
|
3114
2638
|
contentToExtract = templateContent || contentToExtract;
|
|
3115
2639
|
}
|
|
3116
|
-
return contentToExtract;
|
|
3117
|
-
};
|
|
3118
2640
|
|
|
3119
|
-
|
|
3120
|
-
* Handle extract tags
|
|
3121
|
-
*/
|
|
3122
|
-
const handleExtractTags = () => {
|
|
3123
|
-
if (channel === CHANNELS.RCS) {
|
|
3124
|
-
applyRcsSmsFallbackTagExtraction();
|
|
3125
|
-
return;
|
|
3126
|
-
}
|
|
3127
|
-
|
|
3128
|
-
const contentToExtract = getContentForTagExtraction();
|
|
2641
|
+
// Check for personalization tags (excluding unsubscribe)
|
|
3129
2642
|
const tags = contentToExtract.match(/{{[^}]+}}/g) || [];
|
|
3130
2643
|
const hasPersonalizationTags = tags.some((tag) => !tag.includes(UNSUBSCRIBE_TAG_NAME));
|
|
3131
|
-
const onlyUnsubscribe = !hasPersonalizationTags && tags.length === 1 && tags[0].includes(UNSUBSCRIBE_TAG_NAME);
|
|
3132
2644
|
|
|
3133
|
-
if (
|
|
2645
|
+
if (!hasPersonalizationTags && tags.length === 1 && tags[0].includes(UNSUBSCRIBE_TAG_NAME)) {
|
|
2646
|
+
// If only unsubscribe tag is present, show noTagsExtracted message
|
|
3134
2647
|
setTagsExtracted(false);
|
|
3135
2648
|
setRequiredTags([]);
|
|
3136
2649
|
setOptionalTags([]);
|
|
@@ -3138,9 +2651,10 @@ const CommonTestAndPreview = (props) => {
|
|
|
3138
2651
|
return;
|
|
3139
2652
|
}
|
|
3140
2653
|
|
|
2654
|
+
// Extract tags
|
|
3141
2655
|
setTagsExtracted(true);
|
|
3142
2656
|
const { templateSubject, templateContent } = prepareTagExtractionPayload(
|
|
3143
|
-
|
|
2657
|
+
channel,
|
|
3144
2658
|
formData || {},
|
|
3145
2659
|
contentToExtract
|
|
3146
2660
|
);
|
|
@@ -3202,7 +2716,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
3202
2716
|
if (existingTestCustomer) {
|
|
3203
2717
|
const entityId = existingTestCustomer.userId ?? existingTestCustomer.customerId;
|
|
3204
2718
|
if (entityId != null) {
|
|
3205
|
-
const id = normalizeTestEntityId(entityId);
|
|
2719
|
+
const id = 'customer:' + normalizeTestEntityId(entityId);
|
|
3206
2720
|
setSelectedTestEntities((prev) => (
|
|
3207
2721
|
prev.some((existing) => testEntityIdsEqual(existing, id)) ? prev : [...prev, id]
|
|
3208
2722
|
));
|
|
@@ -3239,7 +2753,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
3239
2753
|
(c) => String(c?.customerId) === customerIdFromLookup || String(c?.userId) === customerIdFromLookup
|
|
3240
2754
|
);
|
|
3241
2755
|
if (alreadyInTestListByCustomerId) {
|
|
3242
|
-
const id = normalizeTestEntityId(customerIdFromLookup);
|
|
2756
|
+
const id = 'customer:' + normalizeTestEntityId(customerIdFromLookup);
|
|
3243
2757
|
setSelectedTestEntities((prev) => (
|
|
3244
2758
|
prev.some((existing) => testEntityIdsEqual(existing, id)) ? prev : [...prev, id]
|
|
3245
2759
|
));
|
|
@@ -3276,21 +2790,26 @@ const CommonTestAndPreview = (props) => {
|
|
|
3276
2790
|
const handleSendTestMessage = () => {
|
|
3277
2791
|
const allUserIds = [];
|
|
3278
2792
|
selectedTestEntities.forEach((entityId) => {
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
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
|
+
}
|
|
3282
2799
|
} else {
|
|
3283
|
-
|
|
2800
|
+
const rawId = String(entityId).startsWith('customer:')
|
|
2801
|
+
? String(entityId).slice('customer:'.length)
|
|
2802
|
+
: String(entityId);
|
|
2803
|
+
allUserIds.push(Number(rawId));
|
|
3284
2804
|
}
|
|
3285
2805
|
});
|
|
3286
2806
|
const uniqueUserIds = [...new Set(allUserIds)];
|
|
3287
2807
|
|
|
3288
|
-
const deliveryOverride = [CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP
|
|
2808
|
+
const deliveryOverride = [CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP].includes(channel)
|
|
3289
2809
|
? testPreviewDeliverySettings[channel]
|
|
3290
2810
|
: null;
|
|
3291
2811
|
|
|
3292
|
-
//
|
|
3293
|
-
// Do not use activeChannelForActions / activeContentForActions — those follow the RCS vs Fallback SMS *preview* tab.
|
|
2812
|
+
// Create initial payload based on channel
|
|
3294
2813
|
const initialPayload = prepareTestMessagePayload(
|
|
3295
2814
|
channel,
|
|
3296
2815
|
formData || content || {},
|
|
@@ -3298,8 +2817,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
3298
2817
|
customValues,
|
|
3299
2818
|
uniqueUserIds,
|
|
3300
2819
|
previewData,
|
|
3301
|
-
deliveryOverride
|
|
3302
|
-
{},
|
|
2820
|
+
deliveryOverride
|
|
3303
2821
|
);
|
|
3304
2822
|
|
|
3305
2823
|
actions.createMessageMetaRequested(
|
|
@@ -3332,10 +2850,11 @@ const CommonTestAndPreview = (props) => {
|
|
|
3332
2850
|
// ============================================
|
|
3333
2851
|
// RENDER HELPER FUNCTIONS
|
|
3334
2852
|
// ============================================
|
|
2853
|
+
|
|
3335
2854
|
const renderLeftPanelContent = () => (
|
|
3336
2855
|
<LeftPanelContent
|
|
3337
|
-
isExtractingTags={isExtractingTags
|
|
3338
|
-
extractedTags={
|
|
2856
|
+
isExtractingTags={isExtractingTags}
|
|
2857
|
+
extractedTags={extractedTags}
|
|
3339
2858
|
selectedCustomer={selectedCustomer}
|
|
3340
2859
|
handleCustomerSelect={handleCustomerSelect}
|
|
3341
2860
|
handleSearchCustomer={handleSearchCustomer}
|
|
@@ -3353,29 +2872,15 @@ const CommonTestAndPreview = (props) => {
|
|
|
3353
2872
|
|
|
3354
2873
|
const renderCustomValuesEditor = () => (
|
|
3355
2874
|
<CustomValuesEditor
|
|
3356
|
-
isExtractingTags={isExtractingTags
|
|
2875
|
+
isExtractingTags={isExtractingTags}
|
|
3357
2876
|
isUpdatePreviewDisabled={isUpdatePreviewDisabled}
|
|
3358
2877
|
showJSON={showJSON}
|
|
3359
2878
|
setShowJSON={setShowJSON}
|
|
3360
2879
|
customValues={customValues}
|
|
3361
2880
|
handleJSONTextChange={handleJSONTextChange}
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
title:
|
|
3366
|
-
channel === CHANNELS.RCS
|
|
3367
|
-
? messages.rcsTagsSectionTitle
|
|
3368
|
-
: messages[`${channel}TagsSectionTitle`],
|
|
3369
|
-
requiredTags,
|
|
3370
|
-
optionalTags,
|
|
3371
|
-
},
|
|
3372
|
-
{
|
|
3373
|
-
key: PREVIEW_TAB_SMS_FALLBACK,
|
|
3374
|
-
title: channel === CHANNELS.RCS && smsFallbackContent?.templateContent ? messages.smsFallbackTagsSectionTitle : null,
|
|
3375
|
-
requiredTags: smsFallbackRequiredTags,
|
|
3376
|
-
optionalTags: smsFallbackOptionalTags,
|
|
3377
|
-
},
|
|
3378
|
-
]}
|
|
2881
|
+
extractedTags={extractedTags}
|
|
2882
|
+
requiredTags={requiredTags}
|
|
2883
|
+
optionalTags={optionalTags}
|
|
3379
2884
|
handleCustomValueChange={handleCustomValueChange}
|
|
3380
2885
|
handleDiscardCustomValues={handleDiscardCustomValues}
|
|
3381
2886
|
handleUpdatePreview={handleUpdatePreview}
|
|
@@ -3391,13 +2896,18 @@ const CommonTestAndPreview = (props) => {
|
|
|
3391
2896
|
}));
|
|
3392
2897
|
};
|
|
3393
2898
|
|
|
3394
|
-
/** Trim pasted emails (trailing CR/LF).
|
|
2899
|
+
/** Trim pasted emails (trailing CR/LF). SMS: strip non-digits so pasted formatted numbers match isValidMobile / API. */
|
|
3395
2900
|
const handleTestCustomersSearch = useCallback((value) => {
|
|
3396
2901
|
if (value == null || value === '') {
|
|
3397
2902
|
setSearchValue('');
|
|
3398
2903
|
return;
|
|
3399
2904
|
}
|
|
3400
|
-
|
|
2905
|
+
const raw = String(value).trim();
|
|
2906
|
+
if (channel === CHANNELS.SMS) {
|
|
2907
|
+
setSearchValue(formatPhoneNumber(raw));
|
|
2908
|
+
} else {
|
|
2909
|
+
setSearchValue(raw);
|
|
2910
|
+
}
|
|
3401
2911
|
}, [channel]);
|
|
3402
2912
|
|
|
3403
2913
|
const renderSendTestMessage = () => (
|
|
@@ -3415,13 +2925,12 @@ const CommonTestAndPreview = (props) => {
|
|
|
3415
2925
|
renderAddTestCustomerButton={renderAddTestCustomerButton}
|
|
3416
2926
|
formatMessage={formatMessage}
|
|
3417
2927
|
deliverySettings={testPreviewDeliverySettings[channel]}
|
|
3418
|
-
|
|
2928
|
+
senderDetailsOptions={senderDetailsByChannel[channel]}
|
|
3419
2929
|
wecrmAccounts={wecrmAccounts}
|
|
3420
2930
|
onSaveDeliverySettings={handleSaveDeliverySettings}
|
|
3421
2931
|
isLoadingSenderDetails={isLoadingSenderDetails}
|
|
3422
2932
|
smsTraiDltEnabled={smsTraiDltEnabled}
|
|
3423
2933
|
registeredSenderIds={registeredSenderIds}
|
|
3424
|
-
isChannelSmsFallbackPreviewEnabled={channel === CHANNELS.RCS && !!smsFallbackContent?.templateContent}
|
|
3425
2934
|
searchValue={searchValue}
|
|
3426
2935
|
setSearchValue={handleTestCustomersSearch}
|
|
3427
2936
|
/>
|
|
@@ -3435,13 +2944,14 @@ const CommonTestAndPreview = (props) => {
|
|
|
3435
2944
|
|
|
3436
2945
|
const renderAddTestCustomerButton = () => {
|
|
3437
2946
|
const raw = (searchValue || '').trim();
|
|
2947
|
+
const value = channel === CHANNELS.SMS ? formatPhoneNumber(raw) : raw;
|
|
3438
2948
|
const showAddButton =
|
|
3439
2949
|
[CHANNELS.EMAIL, CHANNELS.SMS].includes(channel) &&
|
|
3440
|
-
(channel === CHANNELS.EMAIL ? isValidEmail(
|
|
2950
|
+
(channel === CHANNELS.EMAIL ? isValidEmail(value) : isValidMobile(value));
|
|
3441
2951
|
if (!showAddButton) return null;
|
|
3442
2952
|
return (
|
|
3443
2953
|
<AddTestCustomerButton
|
|
3444
|
-
searchValue={
|
|
2954
|
+
searchValue={value}
|
|
3445
2955
|
handleAddTestCustomer={handleAddTestCustomer}
|
|
3446
2956
|
/>
|
|
3447
2957
|
);
|