@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.
Files changed (33) hide show
  1. package/.vscode/settings.json +3 -0
  2. package/constants/unified.js +1 -0
  3. package/package.json +1 -1
  4. package/services/api.js +0 -9
  5. package/utils/common.js +4 -0
  6. package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberCarouselPreviewCards.js +127 -0
  7. package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberPreviewContent.js +106 -15
  8. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +139 -1
  9. package/v2Components/CommonTestAndPreview/UnifiedPreview/_viberCarouselPreviewCards.scss +133 -0
  10. package/v2Components/CommonTestAndPreview/constants.js +2 -0
  11. package/v2Components/CommonTestAndPreview/index.js +243 -26
  12. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/ViberPreviewContent.test.js +364 -0
  13. package/v2Components/CommonTestAndPreview/tests/utils.test.js +49 -0
  14. package/v2Components/CommonTestAndPreview/utils.js +20 -0
  15. package/v2Containers/CommunicationFlow/CommunicationFlow.js +58 -93
  16. package/v2Containers/CommunicationFlow/CommunicationFlowCard.js +4 -20
  17. package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +49 -194
  18. package/v2Containers/CommunicationFlow/Tests/CommunicationFlowCard.test.js +0 -16
  19. package/v2Containers/CommunicationFlow/constants.js +0 -47
  20. package/v2Containers/CommunicationFlow/index.js +20 -15
  21. package/v2Containers/CommunicationFlow/messages.js +0 -4
  22. package/v2Containers/CreativesContainer/index.js +2 -0
  23. package/v2Containers/Rcs/constants.js +1 -0
  24. package/v2Containers/Rcs/index.js +9 -1
  25. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +108 -0
  26. package/v2Containers/Rcs/tests/index.test.js +121 -0
  27. package/v2Containers/Templates/_templates.scss +179 -1
  28. package/v2Containers/Templates/index.js +120 -10
  29. package/v2Containers/Viber/constants.js +23 -0
  30. package/v2Containers/Viber/index.js +718 -43
  31. package/v2Containers/Viber/index.scss +175 -0
  32. package/v2Containers/Viber/messages.js +121 -0
  33. 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
- }, [disabled]);
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
- {disabled && disabledTooltip ? (
190
- <CapTooltip title={disabledTooltip}>
191
- <CapButton type="secondary" disabled>
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
- createCommDefinition: jest.fn(),
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 { createCommDefinition } from '../../../services/api';
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
- createCommDefinition.mockResolvedValue({
365
- response: { data: { id: 'cd_123', referenceId: 'ORDER_PLACED_123', status: 'DRAFT', version: { version: 1 } } },
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 createCommDefinition with a SINGLE-strategy payload when useCCS is not false', async () => {
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: { message: 'Hello' } }],
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(createCommDefinition).toHaveBeenCalledTimes(1));
387
- expect(createCommDefinition).toHaveBeenCalledWith(
383
+ await waitFor(() => expect(createCentralCommsMetaId).toHaveBeenCalledTimes(1));
384
+ expect(createCentralCommsMetaId).toHaveBeenCalledWith(
388
385
  expect.objectContaining({
389
- name: 'Order Placed Notification',
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('normalizes the internal MOBILEPUSH channel to CCS\'s MPUSH enum and key names', async () => {
403
- const onSave = jest.fn();
392
+ it('calls getCentralCommsMetaIds when metaIds are returned from createCentralCommsMetaId', async () => {
404
393
  renderWithFlow({
405
394
  features: {},
406
- config: ccsConfig,
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(createCommDefinition).toHaveBeenCalledTimes(1));
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('merges ccsCommDefinition into the data passed to onSave when create succeeds', async () => {
429
- const onSave = jest.fn();
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(onSave).toHaveBeenCalledTimes(1));
440
- expect(onSave).toHaveBeenCalledWith(
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('preserves a real version of 0 from CCS (not coerced to 1 by a falsy-zero fallback)', async () => {
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: ccsConfig,
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(onSave).toHaveBeenCalledTimes(1));
462
- expect(onSave).toHaveBeenCalledWith(
425
+ await waitFor(() => expect(createCentralCommsMetaId).toHaveBeenCalled());
426
+ expect(createCentralCommsMetaId).toHaveBeenCalledWith(
463
427
  expect.objectContaining({
464
- ccsCommDefinition: { id: 'cd_123', referenceId: 'ORDER_PLACED_123', version: 0, status: 'DRAFT' },
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: { ...ccsConfig, useCCS: false },
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(createCommDefinition).not.toHaveBeenCalled();
514
- expect(onSave).toHaveBeenCalledWith(expect.not.objectContaining({ ccsCommDefinition: expect.anything() }));
445
+ expect(createCentralCommsMetaId).not.toHaveBeenCalled();
515
446
  });
516
447
 
517
- it('skips createCommDefinition when config.context.name is absent (mandatory)', async () => {
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 createCommDefinition for multi-channel strategies (CHANNEL_PRIORITY/AB_TEST)', async () => {
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(createCommDefinition).not.toHaveBeenCalled();
473
+ expect(createCentralCommsMetaId).not.toHaveBeenCalled();
601
474
  });
602
475
 
603
- it('places additionalSettings derived from dynamicControls under settings.additionalSettings (not delivery settings)', async () => {
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(createCommDefinition).toHaveBeenCalled());
621
- const payload = createCommDefinition.mock.calls[0][0];
622
- expect(payload.settings.additionalSettings).toEqual({
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('does not crash and skips CCS when config.context is entirely absent', async () => {
706
- const onSave = jest.fn();
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(onSave).toHaveBeenCalledTimes(1));
717
- expect(createCommDefinition).not.toHaveBeenCalled();
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('does not crash when config.context is present but empty', async () => {
721
- const onSave = jest.fn();
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, context: {}, features: {} },
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(onSave).toHaveBeenCalledTimes(1));
732
- expect(createCommDefinition).not.toHaveBeenCalled();
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
- <CommunicationFlow
22
- config={config}
23
- initialData={initialData}
24
- onSave={onSave}
25
- onCancel={onCancel}
26
- onChange={onChange}
27
- {...otherProps}
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; name (required for CCS create),
84
- // referenceId (optional, auto-generated from name if absent), description (optional).
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; data.ccsCommDefinition
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
  : {}),
@@ -32,6 +32,7 @@ export const RCS_STATUSES = {
32
32
 
33
33
  export const HOST_ICS="rcsicsbulk";
34
34
  export const HOST_INFOBIP="rcsinfobipbulk";
35
+ export const HOST_VALUE_FIRST="rcsvaluefirstbulk";
35
36
 
36
37
  export const contentType = {
37
38
  text_message: 'text_message',