@capillarytech/creatives-library 8.0.319 → 8.0.320
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 +14 -0
- package/package.json +1 -1
- package/utils/templateVarUtils.js +172 -0
- package/utils/tests/templateVarUtils.test.js +160 -0
- package/v2Components/CapTagList/index.js +10 -0
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -49
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +207 -21
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +85 -10
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +79 -11
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +11 -5
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +20 -1
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +133 -4
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +12 -0
- package/v2Components/CommonTestAndPreview/constants.js +38 -0
- package/v2Components/CommonTestAndPreview/index.js +693 -155
- package/v2Components/CommonTestAndPreview/messages.js +41 -3
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
- package/v2Components/CommonTestAndPreview/sagas.js +15 -6
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +352 -0
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +269 -1
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +25 -4
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
- package/v2Components/CommonTestAndPreview/tests/index.test.js +132 -4
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
- package/v2Components/FormBuilder/index.js +7 -1
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +87 -0
- package/v2Components/SmsFallback/constants.js +73 -0
- package/v2Components/SmsFallback/index.js +956 -0
- package/v2Components/SmsFallback/index.scss +265 -0
- package/v2Components/SmsFallback/messages.js +78 -0
- package/v2Components/SmsFallback/smsFallbackUtils.js +107 -0
- package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
- package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
- package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
- package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +197 -0
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +261 -0
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
- package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
- package/v2Components/TestAndPreviewSlidebox/index.js +8 -1
- package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
- package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
- package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
- package/v2Components/VarSegmentMessageEditor/index.js +125 -0
- package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
- package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +43 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +36 -4
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +10 -1
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
- package/v2Containers/CreativesContainer/constants.js +9 -0
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +67 -0
- package/v2Containers/CreativesContainer/index.js +289 -99
- package/v2Containers/CreativesContainer/index.scss +51 -1
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +104 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +110 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +363 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -10
- package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
- package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
- package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
- package/v2Containers/Rcs/constants.js +32 -1
- package/v2Containers/Rcs/index.js +950 -873
- package/v2Containers/Rcs/index.scss +85 -6
- package/v2Containers/Rcs/messages.js +10 -1
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +205 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +40834 -1963
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
- package/v2Containers/Rcs/tests/index.test.js +41 -38
- package/v2Containers/Rcs/tests/mockData.js +38 -0
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +251 -0
- package/v2Containers/Rcs/tests/utils.test.js +379 -1
- package/v2Containers/Rcs/utils.js +358 -10
- package/v2Containers/Sms/Create/index.js +81 -36
- package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
- package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
- package/v2Containers/SmsTrai/Create/index.js +9 -4
- package/v2Containers/SmsTrai/Edit/constants.js +2 -0
- package/v2Containers/SmsTrai/Edit/index.js +609 -128
- package/v2Containers/SmsTrai/Edit/index.scss +121 -0
- package/v2Containers/SmsTrai/Edit/messages.js +9 -4
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4327 -2374
- package/v2Containers/SmsWrapper/index.js +37 -8
- package/v2Containers/TagList/index.js +6 -0
- package/v2Containers/Templates/TemplatesActionBar.js +101 -0
- package/v2Containers/Templates/_templates.scss +61 -2
- package/v2Containers/Templates/actions.js +11 -0
- package/v2Containers/Templates/constants.js +2 -0
- package/v2Containers/Templates/index.js +90 -40
- package/v2Containers/Templates/sagas.js +57 -12
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1043 -1079
- package/v2Containers/Templates/tests/sagas.test.js +193 -12
- package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
- package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
- package/v2Containers/TemplatesV2/index.js +86 -23
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
- package/v2Containers/Whatsapp/index.js +3 -20
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
|
@@ -30,23 +30,28 @@ jest.mock('@capillarytech/cap-ui-library/CapNotification', () => ({
|
|
|
30
30
|
}));
|
|
31
31
|
|
|
32
32
|
// Mock child components - must use React.createElement to avoid hoisting issues
|
|
33
|
+
let lastLeftPanelContentProps = null;
|
|
33
34
|
jest.mock('../LeftPanelContent', () => {
|
|
34
35
|
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
|
|
35
36
|
const ReactLib = require('react');
|
|
36
37
|
return {
|
|
37
38
|
__esModule: true,
|
|
38
|
-
default: function MockLeftPanelContent() {
|
|
39
|
-
|
|
39
|
+
default: function MockLeftPanelContent(props) {
|
|
40
|
+
lastLeftPanelContentProps = props;
|
|
41
|
+
const editorEl = props.renderCustomValuesEditor ? props.renderCustomValuesEditor() : null;
|
|
42
|
+
return ReactLib.createElement('div', { 'data-testid': 'left-panel' }, 'Left Panel', editorEl);
|
|
40
43
|
},
|
|
41
44
|
};
|
|
42
45
|
});
|
|
43
46
|
|
|
47
|
+
let lastCustomValuesEditorProps = null;
|
|
44
48
|
jest.mock('../CustomValuesEditor', () => {
|
|
45
49
|
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
|
|
46
50
|
const ReactLib = require('react');
|
|
47
51
|
return {
|
|
48
52
|
__esModule: true,
|
|
49
|
-
default: function MockCustomValuesEditor() {
|
|
53
|
+
default: function MockCustomValuesEditor(props) {
|
|
54
|
+
lastCustomValuesEditorProps = props;
|
|
50
55
|
return ReactLib.createElement('div', { 'data-testid': 'custom-values-editor' }, 'Custom Values Editor');
|
|
51
56
|
},
|
|
52
57
|
};
|
|
@@ -184,6 +189,8 @@ describe('CommonTestAndPreview', () => {
|
|
|
184
189
|
beforeEach(() => {
|
|
185
190
|
jest.clearAllMocks();
|
|
186
191
|
lastSendTestMessageProps = null;
|
|
192
|
+
lastLeftPanelContentProps = null;
|
|
193
|
+
lastCustomValuesEditorProps = null;
|
|
187
194
|
// Reset all mock function implementations
|
|
188
195
|
Object.values(mockActions).forEach((mockFn) => {
|
|
189
196
|
if (jest.isMockFunction(mockFn)) {
|
|
@@ -238,6 +245,24 @@ describe('CommonTestAndPreview', () => {
|
|
|
238
245
|
});
|
|
239
246
|
});
|
|
240
247
|
|
|
248
|
+
it('should call getSenderDetailsRequested for RCS and SMS when channel is RCS', async () => {
|
|
249
|
+
render(
|
|
250
|
+
<TestWrapper>
|
|
251
|
+
<CommonTestAndPreview {...defaultProps} channel={CHANNELS.RCS} />
|
|
252
|
+
</TestWrapper>
|
|
253
|
+
);
|
|
254
|
+
await waitFor(() => {
|
|
255
|
+
expect(mockActions.getSenderDetailsRequested).toHaveBeenCalledWith({
|
|
256
|
+
channel: CHANNELS.RCS,
|
|
257
|
+
orgUnitId: -1,
|
|
258
|
+
});
|
|
259
|
+
expect(mockActions.getSenderDetailsRequested).toHaveBeenCalledWith({
|
|
260
|
+
channel: CHANNELS.SMS,
|
|
261
|
+
orgUnitId: -1,
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
|
|
241
266
|
it('should not call getSenderDetailsRequested when channel is INAPP', async () => {
|
|
242
267
|
render(
|
|
243
268
|
<TestWrapper>
|
|
@@ -296,7 +321,7 @@ describe('CommonTestAndPreview', () => {
|
|
|
296
321
|
});
|
|
297
322
|
expect(lastSendTestMessageProps).toBeDefined();
|
|
298
323
|
expect(lastSendTestMessageProps.deliverySettings).toBeDefined();
|
|
299
|
-
expect(lastSendTestMessageProps.
|
|
324
|
+
expect(lastSendTestMessageProps.senderDetailsByChannel).toEqual(senderDetailsByChannel);
|
|
300
325
|
expect(lastSendTestMessageProps.wecrmAccounts).toEqual([]);
|
|
301
326
|
expect(typeof lastSendTestMessageProps.onSaveDeliverySettings).toBe('function');
|
|
302
327
|
expect(lastSendTestMessageProps.isLoadingSenderDetails).toBe(false);
|
|
@@ -3245,4 +3270,107 @@ describe('CommonTestAndPreview', () => {
|
|
|
3245
3270
|
});
|
|
3246
3271
|
});
|
|
3247
3272
|
});
|
|
3273
|
+
|
|
3274
|
+
describe('SMS DLT and mustache tag discrimination (smsTemplateHasMustacheTags / buildSyntheticSmsMustacheTags)', () => {
|
|
3275
|
+
it('should return no tags for SMS content that contains only DLT {#var#} tokens', async () => {
|
|
3276
|
+
render(
|
|
3277
|
+
<TestWrapper>
|
|
3278
|
+
<CommonTestAndPreview
|
|
3279
|
+
{...defaultProps}
|
|
3280
|
+
channel={CHANNELS.SMS}
|
|
3281
|
+
formData={{ 0: { 'sms-editor': 'Order {#orderId#} is confirmed' } }}
|
|
3282
|
+
extractedTags={[]}
|
|
3283
|
+
/>
|
|
3284
|
+
</TestWrapper>
|
|
3285
|
+
);
|
|
3286
|
+
|
|
3287
|
+
await waitFor(() => expect(lastLeftPanelContentProps).not.toBeNull());
|
|
3288
|
+
|
|
3289
|
+
// smsTemplateHasMustacheTags returns false for DLT-only → extractedTags is []
|
|
3290
|
+
expect(lastLeftPanelContentProps.extractedTags).toEqual([]);
|
|
3291
|
+
});
|
|
3292
|
+
|
|
3293
|
+
it('should build synthetic tags from {{mustache}} tokens, excluding DLT {#var#} tokens', async () => {
|
|
3294
|
+
render(
|
|
3295
|
+
<TestWrapper>
|
|
3296
|
+
<CommonTestAndPreview
|
|
3297
|
+
{...defaultProps}
|
|
3298
|
+
channel={CHANNELS.SMS}
|
|
3299
|
+
formData={{ 0: { 'sms-editor': 'Hi {{name}}, order {#orderId#} shipped' } }}
|
|
3300
|
+
extractedTags={[]}
|
|
3301
|
+
/>
|
|
3302
|
+
</TestWrapper>
|
|
3303
|
+
);
|
|
3304
|
+
|
|
3305
|
+
await waitFor(() => expect(lastLeftPanelContentProps).not.toBeNull());
|
|
3306
|
+
|
|
3307
|
+
const tags = lastLeftPanelContentProps.extractedTags;
|
|
3308
|
+
// Only {{name}} should become a tag — {#orderId#} must be excluded
|
|
3309
|
+
expect(tags).toHaveLength(1);
|
|
3310
|
+
expect(tags[0].name).toBe('name');
|
|
3311
|
+
});
|
|
3312
|
+
|
|
3313
|
+
it('should use API-extracted tags when present instead of building synthetic ones', async () => {
|
|
3314
|
+
const apiTags = [{ name: 'firstName', metaData: { userDriven: true }, children: [] }];
|
|
3315
|
+
render(
|
|
3316
|
+
<TestWrapper>
|
|
3317
|
+
<CommonTestAndPreview
|
|
3318
|
+
{...defaultProps}
|
|
3319
|
+
channel={CHANNELS.SMS}
|
|
3320
|
+
formData={{ 0: { 'sms-editor': 'Hi {{firstName}}' } }}
|
|
3321
|
+
extractedTags={apiTags}
|
|
3322
|
+
/>
|
|
3323
|
+
</TestWrapper>
|
|
3324
|
+
);
|
|
3325
|
+
|
|
3326
|
+
await waitFor(() => expect(lastLeftPanelContentProps).not.toBeNull());
|
|
3327
|
+
|
|
3328
|
+
// API tags take priority over buildSyntheticSmsMustacheTags
|
|
3329
|
+
expect(lastLeftPanelContentProps.extractedTags).toEqual(apiTags);
|
|
3330
|
+
});
|
|
3331
|
+
});
|
|
3332
|
+
|
|
3333
|
+
describe('handleDiscardCustomValues — preview reset', () => {
|
|
3334
|
+
it('should call updatePreviewRequested when handleDiscardCustomValues is invoked', async () => {
|
|
3335
|
+
render(
|
|
3336
|
+
<TestWrapper>
|
|
3337
|
+
<CommonTestAndPreview
|
|
3338
|
+
{...defaultProps}
|
|
3339
|
+
channel={CHANNELS.SMS}
|
|
3340
|
+
formData={{ 0: { 'sms-editor': 'Hi {{name}}' } }}
|
|
3341
|
+
/>
|
|
3342
|
+
</TestWrapper>
|
|
3343
|
+
);
|
|
3344
|
+
|
|
3345
|
+
await waitFor(() => expect(lastCustomValuesEditorProps).not.toBeNull());
|
|
3346
|
+
|
|
3347
|
+
lastCustomValuesEditorProps.handleDiscardCustomValues();
|
|
3348
|
+
|
|
3349
|
+
expect(mockActions.updatePreviewRequested).toHaveBeenCalledTimes(1);
|
|
3350
|
+
});
|
|
3351
|
+
|
|
3352
|
+
it('should call updatePreviewRequested with RCS channel when handleDiscardCustomValues is invoked for RCS with fallback', async () => {
|
|
3353
|
+
render(
|
|
3354
|
+
<TestWrapper>
|
|
3355
|
+
<CommonTestAndPreview
|
|
3356
|
+
{...defaultProps}
|
|
3357
|
+
channel={CHANNELS.RCS}
|
|
3358
|
+
smsFallbackContent={{
|
|
3359
|
+
templateContent: 'Fallback {{name}}',
|
|
3360
|
+
content: 'Fallback {{name}}',
|
|
3361
|
+
}}
|
|
3362
|
+
formData={{}}
|
|
3363
|
+
/>
|
|
3364
|
+
</TestWrapper>
|
|
3365
|
+
);
|
|
3366
|
+
|
|
3367
|
+
await waitFor(() => expect(lastCustomValuesEditorProps).not.toBeNull());
|
|
3368
|
+
|
|
3369
|
+
lastCustomValuesEditorProps.handleDiscardCustomValues();
|
|
3370
|
+
|
|
3371
|
+
// updatePreviewRequested is called; syncSmsFallbackPreview (which hits Api directly) is NOT called
|
|
3372
|
+
expect(mockActions.updatePreviewRequested).toHaveBeenCalledTimes(1);
|
|
3373
|
+
});
|
|
3374
|
+
});
|
|
3248
3375
|
});
|
|
3376
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizePreviewApiPayload,
|
|
3
|
+
extractPreviewFromLiquidResponse,
|
|
4
|
+
getSmsFallbackTextForTagExtraction,
|
|
5
|
+
} from '../previewApiUtils';
|
|
6
|
+
import { RCS_SMS_FALLBACK_VAR_MAPPED_PROP } from '../constants';
|
|
7
|
+
|
|
8
|
+
describe('previewApiUtils', () => {
|
|
9
|
+
describe('normalizePreviewApiPayload', () => {
|
|
10
|
+
it('maps messageBody to resolvedBody when resolvedBody is missing', () => {
|
|
11
|
+
expect(normalizePreviewApiPayload({ messageBody: 'Hello' }).resolvedBody).toBe('Hello');
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe('extractPreviewFromLiquidResponse', () => {
|
|
16
|
+
it('accepts nested data shape', () => {
|
|
17
|
+
const r = { data: { resolvedBody: 'ok' } };
|
|
18
|
+
expect(extractPreviewFromLiquidResponse(r)).toEqual({ resolvedBody: 'ok' });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('accepts top-level preview (typical SMS)', () => {
|
|
22
|
+
const r = { messageBody: 'sms text' };
|
|
23
|
+
expect(extractPreviewFromLiquidResponse(r)).toMatchObject({
|
|
24
|
+
messageBody: 'sms text',
|
|
25
|
+
resolvedBody: 'sms text',
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('returns null when errors array is non-empty', () => {
|
|
30
|
+
expect(extractPreviewFromLiquidResponse({ errors: [{ code: 1 }] })).toBeNull();
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('getSmsFallbackTextForTagExtraction', () => {
|
|
35
|
+
it('returns empty string when context is missing or has no template body', () => {
|
|
36
|
+
expect(getSmsFallbackTextForTagExtraction(null)).toBe('');
|
|
37
|
+
expect(getSmsFallbackTextForTagExtraction({})).toBe('');
|
|
38
|
+
expect(getSmsFallbackTextForTagExtraction({ templateContent: '', content: '' })).toBe('');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('returns raw template when rcsSmsFallbackVarMapped is absent or empty', () => {
|
|
42
|
+
const raw = '{{optout}} {{fullname}} test SMS';
|
|
43
|
+
expect(getSmsFallbackTextForTagExtraction({ templateContent: raw })).toBe(raw);
|
|
44
|
+
expect(getSmsFallbackTextForTagExtraction({
|
|
45
|
+
content: raw,
|
|
46
|
+
[RCS_SMS_FALLBACK_VAR_MAPPED_PROP]: {},
|
|
47
|
+
})).toBe(raw);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('prefers templateContent over content when both are set', () => {
|
|
51
|
+
expect(getSmsFallbackTextForTagExtraction({
|
|
52
|
+
templateContent: 'A',
|
|
53
|
+
content: 'B',
|
|
54
|
+
})).toBe('A');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('applies VarSegment slot map so preview / meta payloads are not stale vs raw template', () => {
|
|
58
|
+
const rawTemplateWithSlots = '{{optout}} tail';
|
|
59
|
+
const rcsSmsFallbackVarMappedSlots = { '{{optout}}_0': 'STOP' };
|
|
60
|
+
const resolvedFallbackText = getSmsFallbackTextForTagExtraction({
|
|
61
|
+
templateContent: rawTemplateWithSlots,
|
|
62
|
+
[RCS_SMS_FALLBACK_VAR_MAPPED_PROP]: rcsSmsFallbackVarMappedSlots,
|
|
63
|
+
});
|
|
64
|
+
expect(resolvedFallbackText).toBe('STOP tail');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
-
call, put, takeLatest, all,
|
|
8
|
+
call, put, takeLatest, takeEvery, all,
|
|
9
9
|
} from 'redux-saga/effects';
|
|
10
10
|
import {
|
|
11
11
|
searchCustomersSaga,
|
|
@@ -1333,7 +1333,7 @@ describe('CommonTestAndPreview Sagas', () => {
|
|
|
1333
1333
|
it('should watch for GET_SENDER_DETAILS_REQUESTED', () => {
|
|
1334
1334
|
const generator = watchGetSenderDetails();
|
|
1335
1335
|
expect(generator.next().value).toEqual(
|
|
1336
|
-
|
|
1336
|
+
takeEvery('app/CommonTestAndPreview/GET_SENDER_DETAILS_REQUESTED', getSenderDetailsSaga)
|
|
1337
1337
|
);
|
|
1338
1338
|
expect(generator.next().done).toBe(true);
|
|
1339
1339
|
});
|
|
@@ -3658,6 +3658,9 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3658
3658
|
channel={channel}
|
|
3659
3659
|
eventContextTags={this.props?.eventContextTags}
|
|
3660
3660
|
restrictPersonalization={this.props.restrictPersonalization}
|
|
3661
|
+
getPopupContainer={this.props.tagListGetPopupContainer}
|
|
3662
|
+
popoverOverlayStyle={this.props.tagListPopoverOverlayStyle}
|
|
3663
|
+
popoverOverlayClassName={this.props.tagListPopoverOverlayClassName}
|
|
3661
3664
|
/>
|
|
3662
3665
|
</CapColumn>
|
|
3663
3666
|
);
|
|
@@ -4348,12 +4351,15 @@ FormBuilder.propTypes = {
|
|
|
4348
4351
|
type: PropTypes.string.isRequired,
|
|
4349
4352
|
isEmailLoading: PropTypes.bool.isRequired,
|
|
4350
4353
|
moduleType: PropTypes.string.isRequired,
|
|
4351
|
-
showLiquidErrorInFooter: PropTypes.
|
|
4354
|
+
showLiquidErrorInFooter: PropTypes.func.isRequired,
|
|
4352
4355
|
eventContextTags: PropTypes.array.isRequired,
|
|
4353
4356
|
forwardedTags: PropTypes.object.isRequired,
|
|
4354
4357
|
isLoyaltyModule: PropTypes.bool.isRequired,
|
|
4355
4358
|
isTestAndPreviewMode: PropTypes.bool, // Add new prop type
|
|
4356
4359
|
restrictPersonalization: PropTypes.bool,
|
|
4360
|
+
tagListGetPopupContainer: PropTypes.func,
|
|
4361
|
+
tagListPopoverOverlayStyle: PropTypes.object,
|
|
4362
|
+
tagListPopoverOverlayClassName: PropTypes.string,
|
|
4357
4363
|
};
|
|
4358
4364
|
|
|
4359
4365
|
const mapStateToProps = createStructuredSelector({
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import CreativesContainer from '../../v2Containers/CreativesContainer';
|
|
4
|
+
import CapPageSpinner from '../CapPageSpinner';
|
|
5
|
+
import {
|
|
6
|
+
EMBEDDED_SMS_CREATIVES_LOCATION,
|
|
7
|
+
SMS_FALLBACK_CHANNEL_KEY,
|
|
8
|
+
SMS_FALLBACK_CREATIVE_EDITOR,
|
|
9
|
+
SMS_FALLBACK_ENABLE_NEW_CHANNELS,
|
|
10
|
+
} from './constants';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Reuse the exact embedded CreativesContainer SMS flow (same as normal SMS create).
|
|
14
|
+
* Avoid overriding Templates with local config so "Create new" follows built-in createTemplate path.
|
|
15
|
+
*/
|
|
16
|
+
export function SmsFallbackLocalSelector({
|
|
17
|
+
hidden,
|
|
18
|
+
fetchDetailsLoading,
|
|
19
|
+
templateList,
|
|
20
|
+
channelsToHide,
|
|
21
|
+
smsRegister,
|
|
22
|
+
onCloseCreatives,
|
|
23
|
+
onSelectTemplate,
|
|
24
|
+
filterContent,
|
|
25
|
+
location,
|
|
26
|
+
/** Required when user completes embedded SMS create/edit — `CreativesContainer` calls this on save (see `processCentralCommsMetaId`). */
|
|
27
|
+
getCreativesData,
|
|
28
|
+
}) {
|
|
29
|
+
const rootClassName = [
|
|
30
|
+
'sms-fallback-selector',
|
|
31
|
+
hidden ? 'sms-fallback-selector--visually-hidden' : '',
|
|
32
|
+
]
|
|
33
|
+
.filter(Boolean)
|
|
34
|
+
.join(' ');
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div className={rootClassName}>
|
|
38
|
+
{fetchDetailsLoading && (
|
|
39
|
+
<div className="sms-fallback-selector__loading" aria-busy="true" aria-live="polite">
|
|
40
|
+
<CapPageSpinner spinning />
|
|
41
|
+
</div>
|
|
42
|
+
)}
|
|
43
|
+
<CreativesContainer
|
|
44
|
+
creativesMode="create"
|
|
45
|
+
location={location || EMBEDDED_SMS_CREATIVES_LOCATION}
|
|
46
|
+
templateData={null}
|
|
47
|
+
handleCloseCreatives={onCloseCreatives}
|
|
48
|
+
isFullMode={false}
|
|
49
|
+
smsRegister={smsRegister}
|
|
50
|
+
editor={SMS_FALLBACK_CREATIVE_EDITOR}
|
|
51
|
+
enableNewChannels={SMS_FALLBACK_ENABLE_NEW_CHANNELS}
|
|
52
|
+
channel={SMS_FALLBACK_CHANNEL_KEY}
|
|
53
|
+
channelsToHide={channelsToHide}
|
|
54
|
+
selectedBadges={[]}
|
|
55
|
+
localTemplatesConfig={{
|
|
56
|
+
useLocalTemplates: true,
|
|
57
|
+
localTemplates: templateList.templates,
|
|
58
|
+
localTemplatesLoading: templateList.loading,
|
|
59
|
+
localTemplatesFilterContent: filterContent,
|
|
60
|
+
localTemplatesOnPageChange: templateList.loadMore,
|
|
61
|
+
localTemplatesUseSkeleton: true,
|
|
62
|
+
}}
|
|
63
|
+
onSelectTemplate={onSelectTemplate}
|
|
64
|
+
getCreativesData={getCreativesData}
|
|
65
|
+
/>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
SmsFallbackLocalSelector.propTypes = {
|
|
71
|
+
hidden: PropTypes.bool,
|
|
72
|
+
fetchDetailsLoading: PropTypes.bool,
|
|
73
|
+
templateList: PropTypes.shape({
|
|
74
|
+
templates: PropTypes.array,
|
|
75
|
+
loading: PropTypes.bool,
|
|
76
|
+
loadMore: PropTypes.func,
|
|
77
|
+
}).isRequired,
|
|
78
|
+
channelsToHide: PropTypes.arrayOf(PropTypes.string),
|
|
79
|
+
smsRegister: PropTypes.any,
|
|
80
|
+
onCloseCreatives: PropTypes.func.isRequired,
|
|
81
|
+
onSelectTemplate: PropTypes.func.isRequired,
|
|
82
|
+
filterContent: PropTypes.node,
|
|
83
|
+
location: PropTypes.object,
|
|
84
|
+
getCreativesData: PropTypes.func.isRequired,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export default SmsFallbackLocalSelector;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {
|
|
2
|
+
WHATSAPP,
|
|
3
|
+
RCS,
|
|
4
|
+
ZALO,
|
|
5
|
+
WEBPUSH,
|
|
6
|
+
} from '../../v2Containers/CreativesContainer/constants';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* View modes for the SMS fallback slidebox (template list, create, edit).
|
|
10
|
+
* Used by SmsFallback and any channel that reuses it (RCS, etc.).
|
|
11
|
+
*/
|
|
12
|
+
export const SMS_FALLBACK_VIEW = {
|
|
13
|
+
SELECTING: 'selecting',
|
|
14
|
+
CREATING: 'creating',
|
|
15
|
+
EDITING: 'editing',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Channel keys to hide in TemplatesV2 so only SMS tab is shown.
|
|
20
|
+
* Uses normalized keys matching TemplatesV2 pane.key normalization (lowercase).
|
|
21
|
+
* Parent can override via channelsToHide prop.
|
|
22
|
+
*/
|
|
23
|
+
export const CHANNELS_TO_HIDE_FOR_SMS_ONLY = [
|
|
24
|
+
'email',
|
|
25
|
+
'mobilepush',
|
|
26
|
+
'webpush',
|
|
27
|
+
'viber',
|
|
28
|
+
'whatsapp',
|
|
29
|
+
'zalo',
|
|
30
|
+
'facebook',
|
|
31
|
+
'rcs',
|
|
32
|
+
'inapp',
|
|
33
|
+
'line',
|
|
34
|
+
'wechat',
|
|
35
|
+
'call_task',
|
|
36
|
+
'ftp',
|
|
37
|
+
'assets',
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
/** DLT category filter values — aligned with Templates `SMS_FILTERS` / `filterSMSTemplates`. */
|
|
41
|
+
export const SMS_CATEGORY_FILTERS = {
|
|
42
|
+
ALL: 'all',
|
|
43
|
+
PROMOTIONAL: 'promo',
|
|
44
|
+
SERVICE_EXPLICIT: 'explicit',
|
|
45
|
+
SERVICE_IMPLICIT: 'implicit',
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** `location` for embedded SMS creatives picker (CreativesContainer / TemplatesV2). */
|
|
49
|
+
export const EMBEDDED_SMS_CREATIVES_LOCATION = {
|
|
50
|
+
pathname: '/sms/create',
|
|
51
|
+
query: { type: 'embedded', module: 'library' },
|
|
52
|
+
search: '',
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/** `location` for embedded SMS TRAI edit inside slidebox. */
|
|
56
|
+
export const EMBEDDED_SMS_CREATIVES_EDIT_LOCATION = {
|
|
57
|
+
pathname: '/sms/edit',
|
|
58
|
+
query: { type: 'embedded', module: 'library' },
|
|
59
|
+
search: '',
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/** API `channel` param for `getTemplateDetails` when loading an SMS template by id. */
|
|
63
|
+
export const SMS_TEMPLATE_DETAILS_API_CHANNEL = 'Sms';
|
|
64
|
+
|
|
65
|
+
export const SMS_FALLBACK_CREATIVE_EDITOR = 'BEE';
|
|
66
|
+
|
|
67
|
+
export const SMS_FALLBACK_CHANNEL_KEY = 'sms';
|
|
68
|
+
|
|
69
|
+
/** Route shape passed to SmsWrapper / SmsTraiEdit for embedded SMS flows. */
|
|
70
|
+
export const SMS_FALLBACK_ROUTE = { name: SMS_FALLBACK_CHANNEL_KEY };
|
|
71
|
+
|
|
72
|
+
/** Channels enabled in the SMS-only embedded picker (hide others via `channelsToHide`). */
|
|
73
|
+
export const SMS_FALLBACK_ENABLE_NEW_CHANNELS = [WHATSAPP, RCS, ZALO, WEBPUSH];
|