@capillarytech/creatives-library 9.0.53-alpha.3 → 9.0.54-0
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/.vscode/settings.json +3 -0
- package/constants/unified.js +1 -0
- package/package.json +1 -1
- package/services/api.js +0 -9
- package/utils/common.js +4 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberCarouselPreviewCards.js +127 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberPreviewContent.js +106 -15
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +139 -1
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_viberCarouselPreviewCards.scss +133 -0
- package/v2Components/CommonTestAndPreview/constants.js +2 -0
- package/v2Components/CommonTestAndPreview/index.js +243 -26
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/ViberPreviewContent.test.js +364 -0
- package/v2Components/CommonTestAndPreview/tests/utils.test.js +49 -0
- package/v2Components/CommonTestAndPreview/utils.js +20 -0
- package/v2Containers/CommunicationFlow/CommunicationFlow.js +58 -93
- package/v2Containers/CommunicationFlow/CommunicationFlowCard.js +4 -20
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +49 -194
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlowCard.test.js +0 -16
- package/v2Containers/CommunicationFlow/constants.js +0 -47
- package/v2Containers/CommunicationFlow/index.js +20 -15
- package/v2Containers/CommunicationFlow/messages.js +0 -4
- package/v2Containers/CreativesContainer/index.js +2 -0
- package/v2Containers/Rcs/constants.js +1 -0
- package/v2Containers/Rcs/index.js +9 -1
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +108 -0
- package/v2Containers/Rcs/tests/index.test.js +121 -0
- package/v2Containers/Templates/_templates.scss +179 -1
- package/v2Containers/Templates/index.js +120 -10
- package/v2Containers/Viber/constants.js +23 -0
- package/v2Containers/Viber/index.js +718 -43
- package/v2Containers/Viber/index.scss +175 -0
- package/v2Containers/Viber/messages.js +121 -0
- package/v2Containers/Viber/tests/index.test.js +80 -0
|
@@ -19,7 +19,6 @@ import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
|
|
|
19
19
|
import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
|
|
20
20
|
import CapHeader from '@capillarytech/cap-ui-library/CapHeader';
|
|
21
21
|
import CapSlideBox from '@capillarytech/cap-ui-library/CapSlideBox';
|
|
22
|
-
import CapTooltip from '@capillarytech/cap-ui-library/CapTooltip';
|
|
23
22
|
import CommunicationFlow from './index';
|
|
24
23
|
import { CHANNELS, DEFAULT_COMMUNICATION_STRATEGY_OPTIONS, DYNAMIC_CONTROLS_CONFIG } from './constants';
|
|
25
24
|
import {
|
|
@@ -75,8 +74,6 @@ const CommunicationFlowCard = ({
|
|
|
75
74
|
onChange,
|
|
76
75
|
cap,
|
|
77
76
|
intl,
|
|
78
|
-
disabled,
|
|
79
|
-
disabledTooltip,
|
|
80
77
|
}) => {
|
|
81
78
|
const { formatMessage } = intl || {};
|
|
82
79
|
const [showSlideBox, setShowSlideBox] = useState(false);
|
|
@@ -94,9 +91,8 @@ const CommunicationFlowCard = ({
|
|
|
94
91
|
}, [onCancel]);
|
|
95
92
|
|
|
96
93
|
const handleOpen = useCallback(() => {
|
|
97
|
-
if (disabled) return;
|
|
98
94
|
setShowSlideBox(true);
|
|
99
|
-
}, [
|
|
95
|
+
}, []);
|
|
100
96
|
|
|
101
97
|
const firstItem = savedData?.contentItems?.[0];
|
|
102
98
|
const channelConfig = firstItem
|
|
@@ -186,17 +182,9 @@ const CommunicationFlowCard = ({
|
|
|
186
182
|
<CapImage src={addCreativesIllustration} />
|
|
187
183
|
</CapColumn>
|
|
188
184
|
<CapColumn span={14} className="empty-card-action-col">
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
{formatMessage(messages.addCreatives)}
|
|
193
|
-
</CapButton>
|
|
194
|
-
</CapTooltip>
|
|
195
|
-
) : (
|
|
196
|
-
<CapButton type="secondary" onClick={handleOpen} disabled={disabled}>
|
|
197
|
-
{formatMessage(messages.addCreatives)}
|
|
198
|
-
</CapButton>
|
|
199
|
-
)}
|
|
185
|
+
<CapButton type="secondary" onClick={handleOpen}>
|
|
186
|
+
{formatMessage(messages.addCreatives)}
|
|
187
|
+
</CapButton>
|
|
200
188
|
</CapColumn>
|
|
201
189
|
</CapRow>
|
|
202
190
|
</CapCard>
|
|
@@ -239,8 +227,6 @@ CommunicationFlowCard.propTypes = {
|
|
|
239
227
|
onChange: PropTypes.func,
|
|
240
228
|
cap: PropTypes.object,
|
|
241
229
|
intl: PropTypes.object.isRequired,
|
|
242
|
-
disabled: PropTypes.bool, // Disables the "Add creatives" trigger (e.g. until a consumer-required field is filled)
|
|
243
|
-
disabledTooltip: PropTypes.node, // Shown on hover when disabled is true
|
|
244
230
|
};
|
|
245
231
|
|
|
246
232
|
CommunicationFlowCard.defaultProps = {
|
|
@@ -249,8 +235,6 @@ CommunicationFlowCard.defaultProps = {
|
|
|
249
235
|
onCancel: null,
|
|
250
236
|
onChange: null,
|
|
251
237
|
cap: null,
|
|
252
|
-
disabled: false,
|
|
253
|
-
disabledTooltip: null,
|
|
254
238
|
};
|
|
255
239
|
|
|
256
240
|
export default injectIntl(CommunicationFlowCard);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
3
|
jest.mock('../../../services/api', () => ({
|
|
4
|
-
|
|
4
|
+
createCentralCommsMetaId: jest.fn(),
|
|
5
|
+
getCentralCommsMetaIds: jest.fn(),
|
|
5
6
|
}));
|
|
6
7
|
|
|
7
8
|
jest.mock('../../CreativesContainer', () => function MockCreativesContainer({
|
|
@@ -37,7 +38,7 @@ import { IntlProvider } from 'react-intl';
|
|
|
37
38
|
import history from '../../../utils/history';
|
|
38
39
|
import { initialReducer } from '../../../initialReducer';
|
|
39
40
|
import CommunicationFlow from '../CommunicationFlow';
|
|
40
|
-
import {
|
|
41
|
+
import { createCentralCommsMetaId, getCentralCommsMetaIds } from '../../../services/api';
|
|
41
42
|
import { getEnabledSteps } from '../utils/getEnabledSteps';
|
|
42
43
|
import {
|
|
43
44
|
CHANNELS,
|
|
@@ -358,151 +359,82 @@ describe('isSaveDisabled', () => {
|
|
|
358
359
|
});
|
|
359
360
|
|
|
360
361
|
describe('handleSave — CCS flow', () => {
|
|
361
|
-
const ccsConfig = { ...baseConfig, context: { name: 'Order Placed Notification' }, features: {} };
|
|
362
|
-
|
|
363
362
|
beforeEach(() => {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
});
|
|
363
|
+
createCentralCommsMetaId.mockResolvedValue({ response: { data: { id: 'meta-123' } } });
|
|
364
|
+
getCentralCommsMetaIds.mockResolvedValue({ response: { data: {} } });
|
|
367
365
|
});
|
|
368
366
|
|
|
369
367
|
afterEach(() => {
|
|
370
368
|
jest.clearAllMocks();
|
|
371
369
|
});
|
|
372
370
|
|
|
373
|
-
it('calls
|
|
371
|
+
it('calls createCentralCommsMetaId for each content item when useCCS is not false', async () => {
|
|
374
372
|
const onSave = jest.fn();
|
|
375
373
|
renderWithFlow({
|
|
376
374
|
features: {},
|
|
377
|
-
config: ccsConfig,
|
|
378
375
|
initialData: {
|
|
379
|
-
contentItems: [{ channel: 'sms', templateData: {
|
|
376
|
+
contentItems: [{ channel: 'sms', templateData: { smsBody: 'Hello' } }],
|
|
380
377
|
},
|
|
381
378
|
onSave,
|
|
382
379
|
});
|
|
383
380
|
|
|
384
381
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
385
382
|
|
|
386
|
-
await waitFor(() => expect(
|
|
387
|
-
expect(
|
|
383
|
+
await waitFor(() => expect(createCentralCommsMetaId).toHaveBeenCalledTimes(1));
|
|
384
|
+
expect(createCentralCommsMetaId).toHaveBeenCalledWith(
|
|
388
385
|
expect.objectContaining({
|
|
389
|
-
|
|
390
|
-
strategyType: 'SINGLE',
|
|
391
|
-
singleChannelStrategy: expect.objectContaining({
|
|
392
|
-
variant: expect.objectContaining({
|
|
393
|
-
channel: 'SMS',
|
|
394
|
-
smsMessageContent: { message: 'Hello' },
|
|
395
|
-
}),
|
|
396
|
-
}),
|
|
386
|
+
centralCommsPayload: expect.objectContaining({ channel: 'SMS', module: 'CAMPAIGNS' }),
|
|
397
387
|
}),
|
|
398
388
|
);
|
|
399
389
|
expect(onSave).toHaveBeenCalledTimes(1);
|
|
400
390
|
});
|
|
401
391
|
|
|
402
|
-
it('
|
|
403
|
-
const onSave = jest.fn();
|
|
392
|
+
it('calls getCentralCommsMetaIds when metaIds are returned from createCentralCommsMetaId', async () => {
|
|
404
393
|
renderWithFlow({
|
|
405
394
|
features: {},
|
|
406
|
-
|
|
407
|
-
initialData: {
|
|
408
|
-
contentItems: [{ channel: 'MOBILEPUSH', templateData: { messageSubject: 'Hi' } }],
|
|
409
|
-
},
|
|
410
|
-
onSave,
|
|
395
|
+
initialData: { contentItems: [{ channel: 'EMAIL', templateData: {} }] },
|
|
411
396
|
});
|
|
412
397
|
|
|
413
398
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
414
399
|
|
|
415
|
-
await waitFor(() => expect(
|
|
416
|
-
expect(createCommDefinition).toHaveBeenCalledWith(
|
|
417
|
-
expect.objectContaining({
|
|
418
|
-
singleChannelStrategy: expect.objectContaining({
|
|
419
|
-
variant: expect.objectContaining({
|
|
420
|
-
channel: 'MPUSH',
|
|
421
|
-
mpushMessageContent: { messageSubject: 'Hi' },
|
|
422
|
-
}),
|
|
423
|
-
}),
|
|
424
|
-
}),
|
|
425
|
-
);
|
|
400
|
+
await waitFor(() => expect(getCentralCommsMetaIds).toHaveBeenCalledWith('meta-123'));
|
|
426
401
|
});
|
|
427
402
|
|
|
428
|
-
it('
|
|
429
|
-
|
|
403
|
+
it('skips getCentralCommsMetaIds when response contains no id', async () => {
|
|
404
|
+
createCentralCommsMetaId.mockResolvedValue({ response: { data: {} } });
|
|
430
405
|
renderWithFlow({
|
|
431
406
|
features: {},
|
|
432
|
-
config: ccsConfig,
|
|
433
407
|
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
434
|
-
onSave,
|
|
435
408
|
});
|
|
436
409
|
|
|
437
410
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
438
411
|
|
|
439
|
-
await waitFor(() => expect(
|
|
440
|
-
expect(
|
|
441
|
-
expect.objectContaining({
|
|
442
|
-
ccsCommDefinition: { id: 'cd_123', referenceId: 'ORDER_PLACED_123', version: 1, status: 'DRAFT' },
|
|
443
|
-
}),
|
|
444
|
-
);
|
|
412
|
+
await waitFor(() => expect(createCentralCommsMetaId).toHaveBeenCalled());
|
|
413
|
+
expect(getCentralCommsMetaIds).not.toHaveBeenCalled();
|
|
445
414
|
});
|
|
446
415
|
|
|
447
|
-
it('
|
|
448
|
-
const onSave = jest.fn();
|
|
449
|
-
createCommDefinition.mockResolvedValueOnce({
|
|
450
|
-
response: { data: { id: 'cd_123', referenceId: 'ORDER_PLACED_123', status: 'DRAFT', version: { version: 0 } } },
|
|
451
|
-
});
|
|
416
|
+
it('uses ouId and module from config.context when provided', async () => {
|
|
452
417
|
renderWithFlow({
|
|
453
418
|
features: {},
|
|
454
|
-
config:
|
|
419
|
+
config: { ...baseConfig, context: { ouId: 42, module: 'LOYALTY' }, features: {} },
|
|
455
420
|
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
456
|
-
onSave,
|
|
457
421
|
});
|
|
458
422
|
|
|
459
423
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
460
424
|
|
|
461
|
-
await waitFor(() => expect(
|
|
462
|
-
expect(
|
|
425
|
+
await waitFor(() => expect(createCentralCommsMetaId).toHaveBeenCalled());
|
|
426
|
+
expect(createCentralCommsMetaId).toHaveBeenCalledWith(
|
|
463
427
|
expect.objectContaining({
|
|
464
|
-
|
|
428
|
+
centralCommsPayload: expect.objectContaining({ ouId: 42, module: 'LOYALTY' }),
|
|
465
429
|
}),
|
|
466
430
|
);
|
|
467
431
|
});
|
|
468
432
|
|
|
469
|
-
it('generates a referenceId from name when config.context.referenceId is absent', async () => {
|
|
470
|
-
// moduleMocks.js spies on Date.now globally, but this config's resetMocks:true
|
|
471
|
-
// clears that return value before every test — set it explicitly here.
|
|
472
|
-
jest.spyOn(Date, 'now').mockReturnValue(1612267539410);
|
|
473
|
-
renderWithFlow({
|
|
474
|
-
features: {},
|
|
475
|
-
config: ccsConfig,
|
|
476
|
-
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
480
|
-
|
|
481
|
-
await waitFor(() => expect(createCommDefinition).toHaveBeenCalled());
|
|
482
|
-
const payload = createCommDefinition.mock.calls[0][0];
|
|
483
|
-
expect(payload.referenceId).toBe('ORDER_PLACED_NOTIFICATION_1612267539410');
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
it('uses config.context.referenceId and description when provided', async () => {
|
|
487
|
-
renderWithFlow({
|
|
488
|
-
features: {},
|
|
489
|
-
config: { ...ccsConfig, context: { ...ccsConfig.context, referenceId: 'ORDER_PLACED', description: 'desc' } },
|
|
490
|
-
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
494
|
-
|
|
495
|
-
await waitFor(() => expect(createCommDefinition).toHaveBeenCalled());
|
|
496
|
-
expect(createCommDefinition).toHaveBeenCalledWith(
|
|
497
|
-
expect.objectContaining({ referenceId: 'ORDER_PLACED', description: 'desc' }),
|
|
498
|
-
);
|
|
499
|
-
});
|
|
500
|
-
|
|
501
433
|
it('skips CCS entirely when useCCS is false', async () => {
|
|
502
434
|
const onSave = jest.fn();
|
|
503
435
|
renderWithFlow({
|
|
504
436
|
features: {},
|
|
505
|
-
config: { ...
|
|
437
|
+
config: { ...baseConfig, useCCS: false, features: {} },
|
|
506
438
|
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
507
439
|
onSave,
|
|
508
440
|
});
|
|
@@ -510,15 +442,14 @@ describe('handleSave — CCS flow', () => {
|
|
|
510
442
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
511
443
|
|
|
512
444
|
await waitFor(() => expect(onSave).toHaveBeenCalledTimes(1));
|
|
513
|
-
expect(
|
|
514
|
-
expect(onSave).toHaveBeenCalledWith(expect.not.objectContaining({ ccsCommDefinition: expect.anything() }));
|
|
445
|
+
expect(createCentralCommsMetaId).not.toHaveBeenCalled();
|
|
515
446
|
});
|
|
516
447
|
|
|
517
|
-
it('
|
|
448
|
+
it('still calls onSave when createCentralCommsMetaId rejects', async () => {
|
|
449
|
+
createCentralCommsMetaId.mockRejectedValue(new Error('Network error'));
|
|
518
450
|
const onSave = jest.fn();
|
|
519
451
|
renderWithFlow({
|
|
520
452
|
features: {},
|
|
521
|
-
config: { ...baseConfig, features: {} },
|
|
522
453
|
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
523
454
|
onSave,
|
|
524
455
|
});
|
|
@@ -526,70 +457,12 @@ describe('handleSave — CCS flow', () => {
|
|
|
526
457
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
527
458
|
|
|
528
459
|
await waitFor(() => expect(onSave).toHaveBeenCalledTimes(1));
|
|
529
|
-
expect(createCommDefinition).not.toHaveBeenCalled();
|
|
530
460
|
});
|
|
531
461
|
|
|
532
|
-
it('skips
|
|
462
|
+
it('skips createCentralCommsMetaId when contentItems is empty', async () => {
|
|
533
463
|
const onSave = jest.fn();
|
|
534
464
|
renderWithFlow({
|
|
535
465
|
features: {},
|
|
536
|
-
config: ccsConfig,
|
|
537
|
-
initialData: {
|
|
538
|
-
communicationStrategy: CHANNEL_PRIORITY,
|
|
539
|
-
channels: ['SMS', 'EMAIL'],
|
|
540
|
-
contentItems: [{ channel: 'SMS', templateData: {} }, { channel: 'EMAIL', templateData: {} }],
|
|
541
|
-
},
|
|
542
|
-
onSave,
|
|
543
|
-
});
|
|
544
|
-
|
|
545
|
-
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
546
|
-
|
|
547
|
-
await waitFor(() => expect(onSave).toHaveBeenCalledTimes(1));
|
|
548
|
-
expect(createCommDefinition).not.toHaveBeenCalled();
|
|
549
|
-
});
|
|
550
|
-
|
|
551
|
-
it('still calls onSave (without ccsCommDefinition) when createCommDefinition rejects', async () => {
|
|
552
|
-
createCommDefinition.mockRejectedValue(new Error('Network error'));
|
|
553
|
-
const onSave = jest.fn();
|
|
554
|
-
renderWithFlow({
|
|
555
|
-
features: {},
|
|
556
|
-
config: ccsConfig,
|
|
557
|
-
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
558
|
-
onSave,
|
|
559
|
-
});
|
|
560
|
-
|
|
561
|
-
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
562
|
-
|
|
563
|
-
await waitFor(() => expect(onSave).toHaveBeenCalledTimes(1));
|
|
564
|
-
expect(onSave).toHaveBeenCalledWith(expect.not.objectContaining({ ccsCommDefinition: expect.anything() }));
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
it('blocks save and shows an error when createCommDefinition resolves with a duplicate REFERENCE_ID_EXISTS conflict', async () => {
|
|
568
|
-
createCommDefinition.mockResolvedValue({
|
|
569
|
-
success: false,
|
|
570
|
-
status: { isError: true, code: 409, message: 'REFERENCE_ID_EXISTS' },
|
|
571
|
-
message: 'REFERENCE_ID_EXISTS',
|
|
572
|
-
});
|
|
573
|
-
const onSave = jest.fn();
|
|
574
|
-
renderWithFlow({
|
|
575
|
-
features: {},
|
|
576
|
-
config: ccsConfig,
|
|
577
|
-
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
578
|
-
onSave,
|
|
579
|
-
});
|
|
580
|
-
|
|
581
|
-
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
582
|
-
|
|
583
|
-
await waitFor(() => expect(createCommDefinition).toHaveBeenCalledTimes(1));
|
|
584
|
-
expect(await screen.findByText(/already in use in your organization/i)).toBeInTheDocument();
|
|
585
|
-
expect(onSave).not.toHaveBeenCalled();
|
|
586
|
-
});
|
|
587
|
-
|
|
588
|
-
it('skips createCommDefinition when contentItems is empty', async () => {
|
|
589
|
-
const onSave = jest.fn();
|
|
590
|
-
renderWithFlow({
|
|
591
|
-
features: {},
|
|
592
|
-
config: ccsConfig,
|
|
593
466
|
initialData: { contentItems: [] },
|
|
594
467
|
onSave,
|
|
595
468
|
});
|
|
@@ -597,13 +470,12 @@ describe('handleSave — CCS flow', () => {
|
|
|
597
470
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
598
471
|
|
|
599
472
|
await waitFor(() => expect(onSave).toHaveBeenCalledTimes(1));
|
|
600
|
-
expect(
|
|
473
|
+
expect(createCentralCommsMetaId).not.toHaveBeenCalled();
|
|
601
474
|
});
|
|
602
475
|
|
|
603
|
-
it('
|
|
476
|
+
it('includes additionalSettings derived from dynamicControls in the payload', async () => {
|
|
604
477
|
renderWithFlow({
|
|
605
478
|
features: {},
|
|
606
|
-
config: ccsConfig,
|
|
607
479
|
initialData: {
|
|
608
480
|
contentItems: [{ channel: 'SMS', templateData: {} }],
|
|
609
481
|
dynamicControls: {
|
|
@@ -617,36 +489,14 @@ describe('handleSave — CCS flow', () => {
|
|
|
617
489
|
|
|
618
490
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
619
491
|
|
|
620
|
-
await waitFor(() => expect(
|
|
621
|
-
const payload =
|
|
622
|
-
expect(payload.
|
|
492
|
+
await waitFor(() => expect(createCentralCommsMetaId).toHaveBeenCalled());
|
|
493
|
+
const payload = createCentralCommsMetaId.mock.calls[0][0];
|
|
494
|
+
expect(payload.centralCommsPayload.smsDeliverySettings.additionalSettings).toEqual({
|
|
623
495
|
useTinyUrl: true,
|
|
624
496
|
encryptUrl: true,
|
|
625
497
|
linkTrackingEnabled: true,
|
|
626
498
|
userSubscriptionDisabled: true,
|
|
627
499
|
});
|
|
628
|
-
expect(payload.singleChannelStrategy.variant.smsDeliverySettings.additionalSettings).toBeUndefined();
|
|
629
|
-
});
|
|
630
|
-
|
|
631
|
-
it('includes channelSettings from deliverySetting.channelSetting in the payload', async () => {
|
|
632
|
-
renderWithFlow({
|
|
633
|
-
features: {},
|
|
634
|
-
config: ccsConfig,
|
|
635
|
-
initialData: {
|
|
636
|
-
contentItems: [{ channel: 'SMS', templateData: {} }],
|
|
637
|
-
deliverySetting: { channelSetting: { SMS: { domainId: 1001, gsmSenderId: 'BRAND1' } } },
|
|
638
|
-
},
|
|
639
|
-
});
|
|
640
|
-
|
|
641
|
-
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
642
|
-
|
|
643
|
-
await waitFor(() => expect(createCommDefinition).toHaveBeenCalled());
|
|
644
|
-
const payload = createCommDefinition.mock.calls[0][0];
|
|
645
|
-
expect(payload.singleChannelStrategy.variant.smsDeliverySettings.channelSettings).toEqual({
|
|
646
|
-
channel: 'SMS',
|
|
647
|
-
domainId: 1001,
|
|
648
|
-
gsmSenderId: 'BRAND1',
|
|
649
|
-
});
|
|
650
500
|
});
|
|
651
501
|
});
|
|
652
502
|
|
|
@@ -702,34 +552,39 @@ describe('optional chaining safety', () => {
|
|
|
702
552
|
jest.clearAllMocks();
|
|
703
553
|
});
|
|
704
554
|
|
|
705
|
-
it('
|
|
706
|
-
|
|
555
|
+
it('defaults ouId to -1 when config.context is absent', async () => {
|
|
556
|
+
createCentralCommsMetaId.mockResolvedValue({ response: { data: { id: 'x' } } });
|
|
707
557
|
renderWithFlow({
|
|
708
558
|
features: {},
|
|
709
|
-
config: { ...baseConfig, features: {} },
|
|
710
559
|
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
711
|
-
onSave,
|
|
712
560
|
});
|
|
713
561
|
|
|
714
562
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
715
563
|
|
|
716
|
-
await waitFor(() => expect(
|
|
717
|
-
expect(
|
|
564
|
+
await waitFor(() => expect(createCentralCommsMetaId).toHaveBeenCalled());
|
|
565
|
+
expect(createCentralCommsMetaId).toHaveBeenCalledWith(
|
|
566
|
+
expect.objectContaining({
|
|
567
|
+
centralCommsPayload: expect.objectContaining({ ouId: -1 }),
|
|
568
|
+
}),
|
|
569
|
+
);
|
|
718
570
|
});
|
|
719
571
|
|
|
720
|
-
it('
|
|
721
|
-
|
|
572
|
+
it('defaults module to consumer.toUpperCase() when config.context.module absent', async () => {
|
|
573
|
+
createCentralCommsMetaId.mockResolvedValue({ response: { data: { id: 'x' } } });
|
|
722
574
|
renderWithFlow({
|
|
723
575
|
features: {},
|
|
724
|
-
config: { ...baseConfig,
|
|
576
|
+
config: { ...baseConfig, consumer: 'loyalty', features: {} },
|
|
725
577
|
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
726
|
-
onSave,
|
|
727
578
|
});
|
|
728
579
|
|
|
729
580
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
730
581
|
|
|
731
|
-
await waitFor(() => expect(
|
|
732
|
-
expect(
|
|
582
|
+
await waitFor(() => expect(createCentralCommsMetaId).toHaveBeenCalled());
|
|
583
|
+
expect(createCentralCommsMetaId).toHaveBeenCalledWith(
|
|
584
|
+
expect.objectContaining({
|
|
585
|
+
centralCommsPayload: expect.objectContaining({ module: 'LOYALTY' }),
|
|
586
|
+
}),
|
|
587
|
+
);
|
|
733
588
|
});
|
|
734
589
|
|
|
735
590
|
it('initializes channel from config.channel when initialData.channel is absent', () => {
|
|
@@ -108,22 +108,6 @@ describe('CommunicationFlowCard', () => {
|
|
|
108
108
|
fireEvent.click(screen.getByText('Add creatives'));
|
|
109
109
|
expect(screen.getByTestId('comm-flow-mock')).toHaveAttribute('data-mode', 'create');
|
|
110
110
|
});
|
|
111
|
-
|
|
112
|
-
it('disables the Add creatives button when disabled prop is true', () => {
|
|
113
|
-
renderCard({ disabled: true, disabledTooltip: 'Enter an Alert name to add content' });
|
|
114
|
-
expect(screen.getByText('Add creatives').closest('button')).toBeDisabled();
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('does not open SlideBox when Add creatives is clicked while disabled', () => {
|
|
118
|
-
renderCard({ disabled: true, disabledTooltip: 'Enter an Alert name to add content' });
|
|
119
|
-
fireEvent.click(screen.getByText('Add creatives'));
|
|
120
|
-
expect(screen.queryByTestId('slide-box')).not.toBeInTheDocument();
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it('Add creatives button is enabled by default (disabled prop absent)', () => {
|
|
124
|
-
renderCard();
|
|
125
|
-
expect(screen.getByText('Add creatives').closest('button')).not.toBeDisabled();
|
|
126
|
-
});
|
|
127
111
|
});
|
|
128
112
|
|
|
129
113
|
describe('configured state (initialData provided)', () => {
|
|
@@ -38,53 +38,6 @@ export const CHANNEL_PRIORITY = 'CHANNEL_PRIORITY';
|
|
|
38
38
|
export const AB_TEST = 'AB_TEST';
|
|
39
39
|
export const SINGLE_TEMPLATE = 'SINGLE_TEMPLATE';
|
|
40
40
|
|
|
41
|
-
// CCS CommDefinition strategyType — distinct from SINGLE_TEMPLATE above, which
|
|
42
|
-
// is this UI's own communicationStrategy value. Only SINGLE is supported this
|
|
43
|
-
// phase; CCS create is skipped entirely for CHANNEL_PRIORITY/AB_TEST.
|
|
44
|
-
export const CCS_STRATEGY_TYPE_SINGLE = 'SINGLE';
|
|
45
|
-
|
|
46
|
-
// Mirrors CCS's SingleChannelStrategy field names exactly (Cap Notify API
|
|
47
|
-
// Design doc sample payloads, verified against real create responses). Kept
|
|
48
|
-
// separate from CHANNEL_CONTENT_KEY_MAP/CHANNEL_DELIVERY_KEY_MAP below, which
|
|
49
|
-
// are the legacy messageMeta payload's keys.
|
|
50
|
-
export const CCS_CHANNEL_CONTENT_KEY_MAP = {
|
|
51
|
-
SMS: 'smsMessageContent',
|
|
52
|
-
EMAIL: 'emailMessageContent',
|
|
53
|
-
WHATSAPP: 'whatsappMessageContent',
|
|
54
|
-
MPUSH: 'mpushMessageContent',
|
|
55
|
-
INAPP: 'inAppMessageContent',
|
|
56
|
-
ANDROID: 'androidMessageContent',
|
|
57
|
-
IOS: 'iosMessageContent',
|
|
58
|
-
ZALO: 'zaloMessageContent',
|
|
59
|
-
VIBER: 'viberMessageContent',
|
|
60
|
-
LINE: 'lineMessageContent',
|
|
61
|
-
WEBPUSH: 'webPushMessageContent',
|
|
62
|
-
RCS: 'rcsMessageContent',
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
export const CCS_CHANNEL_DELIVERY_KEY_MAP = {
|
|
66
|
-
SMS: 'smsDeliverySettings',
|
|
67
|
-
EMAIL: 'emailDeliverySettings',
|
|
68
|
-
WHATSAPP: 'whatsappDeliverySettings',
|
|
69
|
-
MPUSH: 'mpushDeliverySettings',
|
|
70
|
-
INAPP: 'inAppDeliverySettings',
|
|
71
|
-
ANDROID: 'androidDeliverySettings',
|
|
72
|
-
IOS: 'iosDeliverySettings',
|
|
73
|
-
ZALO: 'zaloDeliverySettings',
|
|
74
|
-
VIBER: 'viberDeliverySettings',
|
|
75
|
-
LINE: 'lineDeliverySettings',
|
|
76
|
-
WEBPUSH: 'webPushDeliverySettings',
|
|
77
|
-
RCS: 'rcsDeliverySettings',
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
// The UI's own internal channel identifier for mobile push is 'MOBILEPUSH'
|
|
81
|
-
// (see CreativesContainer/constants.js MOBILE_PUSH), but CCS's channel enum
|
|
82
|
-
// only recognises 'MPUSH' (globals.js CCS_CHANNELS) — normalize before
|
|
83
|
-
// building the CCS payload. Every other channel identifier matches CCS as-is.
|
|
84
|
-
export const CCS_CHANNEL_NAME_MAP = {
|
|
85
|
-
MOBILEPUSH: 'MPUSH',
|
|
86
|
-
};
|
|
87
|
-
|
|
88
41
|
// Channel config - single source of truth for CreativesContainer and TemplatesV2
|
|
89
42
|
// paneKey: TemplatesV2 defaultPanes object key (for channelsToHide)
|
|
90
43
|
// channelProp: CreativesContainer channel prop (must match pane.key for tab to be active)
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import React from 'react';
|
|
10
10
|
import PropTypes from 'prop-types';
|
|
11
11
|
import CommunicationFlow from './CommunicationFlow';
|
|
12
|
+
import { hasSupportEngagementModule } from '../../utils/common';
|
|
12
13
|
|
|
13
14
|
const CommunicationFlowContainer = ({
|
|
14
15
|
config,
|
|
@@ -17,16 +18,22 @@ const CommunicationFlowContainer = ({
|
|
|
17
18
|
onCancel,
|
|
18
19
|
onChange,
|
|
19
20
|
...otherProps
|
|
20
|
-
}) =>
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
}) => {
|
|
22
|
+
if (!hasSupportEngagementModule()) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<CommunicationFlow
|
|
28
|
+
config={config}
|
|
29
|
+
initialData={initialData}
|
|
30
|
+
onSave={onSave}
|
|
31
|
+
onCancel={onCancel}
|
|
32
|
+
onChange={onChange}
|
|
33
|
+
{...otherProps}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
30
37
|
|
|
31
38
|
CommunicationFlowContainer.propTypes = {
|
|
32
39
|
config: PropTypes.shape({
|
|
@@ -80,13 +87,11 @@ CommunicationFlowContainer.propTypes = {
|
|
|
80
87
|
controls: PropTypes.array,
|
|
81
88
|
}),
|
|
82
89
|
}),
|
|
83
|
-
context: PropTypes.object, // ouId, campaignId, programId
|
|
84
|
-
//
|
|
85
|
-
useCCS: PropTypes.bool, // If false, skips the CCS createCommDefinition call on save. Defaults to true.
|
|
90
|
+
context: PropTypes.object, // ouId, campaignId, programId, etc.
|
|
91
|
+
useCCS: PropTypes.bool, // If false, skips CCS bulk-claim-approve on save. Defaults to true.
|
|
86
92
|
}).isRequired,
|
|
87
93
|
initialData: PropTypes.object, // for edit/preview mode
|
|
88
|
-
onSave: PropTypes.func.isRequired, // (data) => void - called when user saves
|
|
89
|
-
// ({id, referenceId, version, status}) is present when the CCS create succeeded
|
|
94
|
+
onSave: PropTypes.func.isRequired, // (data) => void - called when user saves
|
|
90
95
|
onCancel: PropTypes.func.isRequired, // () => void - called when user cancels
|
|
91
96
|
onChange: PropTypes.func, // (data) => void - optional, called on data changes
|
|
92
97
|
};
|
|
@@ -363,8 +363,4 @@ export default {
|
|
|
363
363
|
id: `${prefix}.senderNotConfiguredError`,
|
|
364
364
|
defaultMessage: 'Selected domain gateway id is not correct. Please change the domain id or contact the gateway team to register them with Capillary.',
|
|
365
365
|
},
|
|
366
|
-
duplicateReferenceIdError: {
|
|
367
|
-
id: `${prefix}.duplicateReferenceIdError`,
|
|
368
|
-
defaultMessage: 'This reference ID is already in use in your organization. Please use a different reference ID.',
|
|
369
|
-
},
|
|
370
366
|
};
|
|
@@ -1441,6 +1441,7 @@ export class Creatives extends React.Component {
|
|
|
1441
1441
|
cardType = "",
|
|
1442
1442
|
cardSettings = {},
|
|
1443
1443
|
accountId = "",
|
|
1444
|
+
vfTemplateId,
|
|
1444
1445
|
} = get(versions, 'base.content.RCS.rcsContent', {});
|
|
1445
1446
|
const firstCardFromSubmit = Array.isArray(cardContentFromSubmit)
|
|
1446
1447
|
? cardContentFromSubmit[0]
|
|
@@ -1469,6 +1470,7 @@ export class Creatives extends React.Component {
|
|
|
1469
1470
|
creativeName: name,
|
|
1470
1471
|
rcsContent,
|
|
1471
1472
|
accountId,
|
|
1473
|
+
...(vfTemplateId && { templateId: vfTemplateId }),
|
|
1472
1474
|
...(includeRootRcsCardVarMappedOnConsumerPayload
|
|
1473
1475
|
? { rcsCardVarMapped: cardVarMappedFromFirstRcsCard }
|
|
1474
1476
|
: {}),
|