@capillarytech/creatives-library 9.0.15-alpha.3 → 9.0.16-beta.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 (146) hide show
  1. package/AppRoot.js +80 -0
  2. package/app.js +5 -3
  3. package/constants/unified.js +0 -29
  4. package/entry.js +67 -1
  5. package/global-styles.js +1 -1
  6. package/mfe-exposed-components.js +2 -4
  7. package/package.json +2 -2
  8. package/services/api.js +1 -1
  9. package/services/tests/api.test.js +20 -35
  10. package/styles/containers/layout/_layoutPage.scss +8 -6
  11. package/utils/commonUtils.js +1 -19
  12. package/utils/getDataLayer.js +15 -0
  13. package/utils/gtmTrackers/gtmEvents/creativeDetails.js +2 -1
  14. package/utils/mfeDetect.js +1 -0
  15. package/v2Components/CapActionButton/constants.js +1 -8
  16. package/v2Components/CapActionButton/index.js +144 -202
  17. package/v2Components/CapActionButton/index.scss +14 -232
  18. package/v2Components/CapActionButton/messages.js +3 -19
  19. package/v2Components/CapActionButton/tests/index.test.js +18 -42
  20. package/v2Components/CapImageUpload/index.js +2 -2
  21. package/v2Components/CapTagList/index.js +0 -10
  22. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +48 -70
  23. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
  24. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -214
  25. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
  26. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +9 -83
  27. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
  28. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -60
  29. package/v2Components/CommonTestAndPreview/SendTestMessage.js +5 -10
  30. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +22 -202
  31. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +80 -381
  32. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +8 -155
  33. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +0 -11
  34. package/v2Components/CommonTestAndPreview/constants.js +2 -38
  35. package/v2Components/CommonTestAndPreview/index.js +222 -761
  36. package/v2Components/CommonTestAndPreview/messages.js +3 -45
  37. package/v2Components/CommonTestAndPreview/sagas.js +6 -25
  38. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +0 -1
  39. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +267 -284
  40. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +65 -231
  41. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +5 -118
  42. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -341
  43. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +1 -8
  44. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +13 -34
  45. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +283 -281
  46. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
  47. package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -133
  48. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +24 -31
  49. package/v2Components/NavigationBar/index.js +9 -7
  50. package/v2Components/NavigationBar/mfeModuleHeader.config.js +16 -0
  51. package/v2Components/Pagination/_pagination.scss +2 -0
  52. package/v2Components/TemplatePreview/_templatePreview.scss +25 -45
  53. package/v2Components/TemplatePreview/index.js +32 -147
  54. package/v2Components/TemplatePreview/tests/index.test.js +0 -142
  55. package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +6 -6
  56. package/v2Components/TestAndPreviewSlidebox/index.js +1 -13
  57. package/v2Components/TestAndPreviewSlidebox/sagas.js +4 -11
  58. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +1 -3
  59. package/v2Containers/Cap/constants.js +1 -0
  60. package/v2Containers/Cap/index.js +47 -21
  61. package/v2Containers/Cap/sagas.js +19 -0
  62. package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +1 -4
  63. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +0 -4
  64. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +20 -20
  65. package/v2Containers/CreativesContainer/SlideBoxContent.js +7 -37
  66. package/v2Containers/CreativesContainer/SlideBoxFooter.js +5 -14
  67. package/v2Containers/CreativesContainer/SlideBoxHeader.js +5 -36
  68. package/v2Containers/CreativesContainer/constants.js +0 -11
  69. package/v2Containers/CreativesContainer/index.js +104 -323
  70. package/v2Containers/CreativesContainer/index.scss +1 -83
  71. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -79
  72. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
  73. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
  74. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +90 -333
  75. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +15 -20
  76. package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
  77. package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
  78. package/v2Containers/Rcs/constants.js +11 -131
  79. package/v2Containers/Rcs/index.js +829 -2682
  80. package/v2Containers/Rcs/index.scss +8 -443
  81. package/v2Containers/Rcs/messages.js +6 -45
  82. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +39409 -74176
  83. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +74 -10
  84. package/v2Containers/Rcs/tests/index.test.js +40 -151
  85. package/v2Containers/Rcs/tests/mockData.js +0 -38
  86. package/v2Containers/Rcs/tests/utils.test.js +30 -643
  87. package/v2Containers/Rcs/utils.js +12 -466
  88. package/v2Containers/Sms/Create/index.js +48 -115
  89. package/v2Containers/SmsTrai/Create/index.js +4 -9
  90. package/v2Containers/SmsTrai/Edit/constants.js +0 -2
  91. package/v2Containers/SmsTrai/Edit/index.js +169 -678
  92. package/v2Containers/SmsTrai/Edit/messages.js +4 -14
  93. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2854 -5455
  94. package/v2Containers/SmsWrapper/index.js +8 -37
  95. package/v2Containers/TagList/index.js +0 -6
  96. package/v2Containers/Templates/_templates.scss +11 -196
  97. package/v2Containers/Templates/actions.js +0 -11
  98. package/v2Containers/Templates/constants.js +0 -2
  99. package/v2Containers/Templates/index.js +60 -131
  100. package/v2Containers/Templates/sagas.js +13 -57
  101. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1015 -1060
  102. package/v2Containers/Templates/tests/sagas.test.js +16 -199
  103. package/v2Containers/TemplatesV2/TemplatesV2.style.js +10 -75
  104. package/v2Containers/TemplatesV2/index.js +43 -86
  105. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
  106. package/v2Containers/Whatsapp/index.js +25 -4
  107. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -578
  108. package/utils/rcsPayloadUtils.js +0 -92
  109. package/utils/templateVarUtils.js +0 -201
  110. package/utils/tests/rcsPayloadUtils.test.js +0 -226
  111. package/utils/tests/templateVarUtils.test.js +0 -204
  112. package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
  113. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
  114. package/v2Components/CommonTestAndPreview/utils.js +0 -84
  115. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -91
  116. package/v2Components/SmsFallback/constants.js +0 -94
  117. package/v2Components/SmsFallback/index.js +0 -958
  118. package/v2Components/SmsFallback/index.scss +0 -266
  119. package/v2Components/SmsFallback/messages.js +0 -78
  120. package/v2Components/SmsFallback/smsFallbackUtils.js +0 -120
  121. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
  122. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
  123. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
  124. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -208
  125. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -309
  126. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
  127. package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
  128. package/v2Components/TemplatePreview/constants.js +0 -2
  129. package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
  130. package/v2Components/VarSegmentMessageEditor/index.js +0 -125
  131. package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
  132. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -17
  133. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -79
  134. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
  135. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
  136. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
  137. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -227
  138. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -318
  139. package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
  140. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
  141. package/v2Containers/SmsTrai/Edit/index.scss +0 -126
  142. package/v2Containers/Templates/TemplatesActionBar.js +0 -101
  143. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
  144. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
  145. package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
  146. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +0 -131
@@ -9,16 +9,7 @@ import { render, screen } from '@testing-library/react';
9
9
  import '@testing-library/jest-dom';
10
10
  import { injectIntl, IntlProvider } from 'react-intl';
11
11
  import UnifiedPreview from '../../UnifiedPreview';
12
- import {
13
- CHANNELS,
14
- DESKTOP,
15
- TABLET,
16
- MOBILE,
17
- ANDROID,
18
- IOS,
19
- PREVIEW_TAB_RCS,
20
- PREVIEW_TAB_SMS_FALLBACK,
21
- } from '../../constants';
12
+ import { CHANNELS, DESKTOP, TABLET, MOBILE, ANDROID, IOS } from '../../constants';
22
13
  import messages from '../../messages';
23
14
 
24
15
  // Convert messages object to format expected by IntlProvider
@@ -571,195 +562,6 @@ describe('UnifiedPreview', () => {
571
562
 
572
563
  expect(screen.getByTestId('rcs-sender-id')).toHaveTextContent('RCS_SENDER');
573
564
  });
574
-
575
- describe('RCS SMS fallback — Test & Preview tabs', () => {
576
- it('without SMS fallback selected, shows only RCS preview (no RCS+SMS tab layout)', () => {
577
- const props = {
578
- ...defaultProps,
579
- channel: CHANNELS.RCS,
580
- content: { rcsTitle: 'Hello RCS' },
581
- smsFallbackContent: undefined,
582
- };
583
-
584
- const { container } = render(
585
- <TestWrapper>
586
- <ComponentToRender {...props} />
587
- </TestWrapper>
588
- );
589
-
590
- expect(container.querySelector('.unified-preview-rcs-tabs')).toBeNull();
591
- expect(screen.getByTestId('rcs-preview')).toBeInTheDocument();
592
- expect(screen.queryByTestId('sms-preview')).not.toBeInTheDocument();
593
- });
594
-
595
- it('with SMS fallback template body, shows RCS and Fallback SMS tabs', () => {
596
- const props = {
597
- ...defaultProps,
598
- channel: CHANNELS.RCS,
599
- content: { rcsTitle: 'Hello RCS' },
600
- smsFallbackContent: {
601
- content: 'SMS fallback body',
602
- templateContent: 'SMS fallback body',
603
- },
604
- };
605
-
606
- const { container } = render(
607
- <TestWrapper>
608
- <ComponentToRender {...props} />
609
- </TestWrapper>
610
- );
611
-
612
- expect(container.querySelector('.unified-preview-rcs-tabs')).toBeInTheDocument();
613
- expect(
614
- screen.getByRole('tab', { name: messages.rcsTab.defaultMessage })
615
- ).toBeInTheDocument();
616
- expect(
617
- screen.getByRole('tab', { name: messages.smsFallbackTab.defaultMessage })
618
- ).toBeInTheDocument();
619
- });
620
-
621
- it('on SMS fallback tab, renders SMS preview with resolved fallback text when smsFallbackResolvedText is set', () => {
622
- const props = {
623
- ...defaultProps,
624
- channel: CHANNELS.RCS,
625
- content: { rcsTitle: 'Hello RCS' },
626
- smsFallbackContent: {
627
- content: '{{var}}',
628
- templateContent: '{{var}}',
629
- },
630
- smsFallbackResolvedText: 'Resolved SMS for preview',
631
- activePreviewTab: PREVIEW_TAB_SMS_FALLBACK,
632
- onPreviewTabChange: jest.fn(),
633
- };
634
-
635
- render(
636
- <TestWrapper>
637
- <ComponentToRender {...props} />
638
- </TestWrapper>
639
- );
640
-
641
- expect(screen.getByTestId('sms-content')).toHaveTextContent('Resolved SMS for preview');
642
- });
643
-
644
- it('on RCS tab (default), renders RCS preview when dual tabs are shown', () => {
645
- const props = {
646
- ...defaultProps,
647
- channel: CHANNELS.RCS,
648
- content: { rcsTitle: 'Only RCS pane' },
649
- smsFallbackContent: { content: 'SMS', templateContent: 'SMS' },
650
- activePreviewTab: PREVIEW_TAB_RCS,
651
- onPreviewTabChange: jest.fn(),
652
- };
653
-
654
- render(
655
- <TestWrapper>
656
- <ComponentToRender {...props} />
657
- </TestWrapper>
658
- );
659
-
660
- expect(screen.getByTestId('rcs-preview')).toBeInTheDocument();
661
- expect(screen.getByTestId('rcs-content')).toHaveTextContent(/Only RCS pane/);
662
- });
663
-
664
- it('on SMS fallback tab, shows raw template when no varmap and no resolved text', () => {
665
- const props = {
666
- ...defaultProps,
667
- channel: CHANNELS.RCS,
668
- content: { rcsTitle: 'Hello RCS' },
669
- smsFallbackContent: {
670
- content: 'Hello {{name}}',
671
- templateContent: 'Hello {{name}}',
672
- // no rcsSmsFallbackVarMapped
673
- },
674
- smsFallbackResolvedText: undefined,
675
- activePreviewTab: PREVIEW_TAB_SMS_FALLBACK,
676
- onPreviewTabChange: jest.fn(),
677
- };
678
-
679
- render(
680
- <TestWrapper>
681
- <ComponentToRender {...props} />
682
- </TestWrapper>
683
- );
684
-
685
- // rawFallbackTemplate is shown directly — {{tags}} remain visible
686
- expect(screen.getByTestId('sms-content')).toHaveTextContent('Hello {{name}}');
687
- });
688
-
689
- it('on SMS fallback tab, treats empty resolved text as absent and shows raw template', () => {
690
- const props = {
691
- ...defaultProps,
692
- channel: CHANNELS.RCS,
693
- content: { rcsTitle: 'Hello RCS' },
694
- smsFallbackContent: {
695
- content: 'Raw {{var}} template',
696
- templateContent: 'Raw {{var}} template',
697
- },
698
- smsFallbackResolvedText: '',
699
- activePreviewTab: PREVIEW_TAB_SMS_FALLBACK,
700
- onPreviewTabChange: jest.fn(),
701
- };
702
-
703
- render(
704
- <TestWrapper>
705
- <ComponentToRender {...props} />
706
- </TestWrapper>
707
- );
708
-
709
- expect(screen.getByTestId('sms-content')).toHaveTextContent('Raw {{var}} template');
710
- });
711
-
712
- it('on SMS fallback tab, applies varmap slot substitution when varmap entries exist and no resolved text', () => {
713
- // getFallbackResolvedContent key format: `${fullToken}_${segmentIndex}`
714
- // 'Hello {{name}}' → segments ['Hello ', '{{name}}'], so {{name}} is at index 1 → key '{{name}}_1'
715
- const props = {
716
- ...defaultProps,
717
- channel: CHANNELS.RCS,
718
- content: { rcsTitle: 'Hello RCS' },
719
- smsFallbackContent: {
720
- content: 'Hello {{name}}',
721
- templateContent: 'Hello {{name}}',
722
- rcsSmsFallbackVarMapped: { '{{name}}_1': 'World' },
723
- },
724
- smsFallbackResolvedText: undefined,
725
- activePreviewTab: PREVIEW_TAB_SMS_FALLBACK,
726
- onPreviewTabChange: jest.fn(),
727
- };
728
-
729
- render(
730
- <TestWrapper>
731
- <ComponentToRender {...props} />
732
- </TestWrapper>
733
- );
734
-
735
- expect(screen.getByTestId('sms-content')).toHaveTextContent('Hello World');
736
- });
737
-
738
- it('on SMS fallback tab, resolved text takes priority over varmap entries', () => {
739
- const props = {
740
- ...defaultProps,
741
- channel: CHANNELS.RCS,
742
- content: { rcsTitle: 'Hello RCS' },
743
- smsFallbackContent: {
744
- content: 'Hello {{name}}',
745
- templateContent: 'Hello {{name}}',
746
- rcsSmsFallbackVarMapped: { '{{name}}_1': 'World' },
747
- },
748
- smsFallbackResolvedText: 'Hello John',
749
- activePreviewTab: PREVIEW_TAB_SMS_FALLBACK,
750
- onPreviewTabChange: jest.fn(),
751
- };
752
-
753
- render(
754
- <TestWrapper>
755
- <ComponentToRender {...props} />
756
- </TestWrapper>
757
- );
758
-
759
- // resolvedText wins over varmap substitution
760
- expect(screen.getByTestId('sms-content')).toHaveTextContent('Hello John');
761
- });
762
- });
763
565
  });
764
566
 
765
567
  describe('Channel Routing - INAPP', () => {
@@ -30,28 +30,23 @@ 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;
34
33
  jest.mock('../LeftPanelContent', () => {
35
34
  // eslint-disable-next-line global-require, import/no-extraneous-dependencies
36
35
  const ReactLib = require('react');
37
36
  return {
38
37
  __esModule: true,
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);
38
+ default: function MockLeftPanelContent() {
39
+ return ReactLib.createElement('div', { 'data-testid': 'left-panel' }, 'Left Panel');
43
40
  },
44
41
  };
45
42
  });
46
43
 
47
- let lastCustomValuesEditorProps = null;
48
44
  jest.mock('../CustomValuesEditor', () => {
49
45
  // eslint-disable-next-line global-require, import/no-extraneous-dependencies
50
46
  const ReactLib = require('react');
51
47
  return {
52
48
  __esModule: true,
53
- default: function MockCustomValuesEditor(props) {
54
- lastCustomValuesEditorProps = props;
49
+ default: function MockCustomValuesEditor() {
55
50
  return ReactLib.createElement('div', { 'data-testid': 'custom-values-editor' }, 'Custom Values Editor');
56
51
  },
57
52
  };
@@ -196,8 +191,6 @@ describe('CommonTestAndPreview', () => {
196
191
  beforeEach(() => {
197
192
  jest.clearAllMocks();
198
193
  lastSendTestMessageProps = null;
199
- lastLeftPanelContentProps = null;
200
- lastCustomValuesEditorProps = null;
201
194
  // Reset all mock function implementations
202
195
  Object.values(mockActions).forEach((mockFn) => {
203
196
  if (jest.isMockFunction(mockFn)) {
@@ -252,25 +245,6 @@ describe('CommonTestAndPreview', () => {
252
245
  });
253
246
  });
254
247
 
255
- it('should call getSenderDetailsRequested for RCS and SMS when channel is RCS', async () => {
256
- render(
257
- <TestWrapper>
258
- <CommonTestAndPreview {...defaultProps} channel={CHANNELS.RCS} />
259
- </TestWrapper>
260
- );
261
- await waitFor(() => {
262
- expect(mockActions.getSenderDetailsRequested).toHaveBeenCalledTimes(2);
263
- expect(mockActions.getSenderDetailsRequested).toHaveBeenNthCalledWith(1, {
264
- channel: CHANNELS.RCS,
265
- orgUnitId: -1,
266
- });
267
- expect(mockActions.getSenderDetailsRequested).toHaveBeenNthCalledWith(2, {
268
- channel: CHANNELS.SMS,
269
- orgUnitId: -1,
270
- });
271
- });
272
- });
273
-
274
248
  it('should not call getSenderDetailsRequested when channel is INAPP', async () => {
275
249
  render(
276
250
  <TestWrapper>
@@ -329,7 +303,7 @@ describe('CommonTestAndPreview', () => {
329
303
  });
330
304
  expect(lastSendTestMessageProps).toBeDefined();
331
305
  expect(lastSendTestMessageProps.deliverySettings).toBeDefined();
332
- expect(lastSendTestMessageProps.senderDetailsByChannel).toEqual(senderDetailsByChannel);
306
+ expect(lastSendTestMessageProps.senderDetailsOptions).toEqual(senderDetailsByChannel[CHANNELS.SMS]);
333
307
  expect(lastSendTestMessageProps.wecrmAccounts).toEqual([]);
334
308
  expect(typeof lastSendTestMessageProps.onSaveDeliverySettings).toBe('function');
335
309
  expect(lastSendTestMessageProps.isLoadingSenderDetails).toBe(false);
@@ -3501,107 +3475,4 @@ describe('CommonTestAndPreview', () => {
3501
3475
  });
3502
3476
  });
3503
3477
  });
3504
-
3505
- describe('SMS DLT and mustache tag discrimination (smsTemplateHasMustacheTags / buildSyntheticSmsMustacheTags)', () => {
3506
- it('should return no tags for SMS content that contains only DLT {#var#} tokens', async () => {
3507
- render(
3508
- <TestWrapper>
3509
- <CommonTestAndPreview
3510
- {...defaultProps}
3511
- channel={CHANNELS.SMS}
3512
- formData={{ 0: { 'sms-editor': 'Order {#orderId#} is confirmed' } }}
3513
- extractedTags={[]}
3514
- />
3515
- </TestWrapper>
3516
- );
3517
-
3518
- await waitFor(() => expect(lastLeftPanelContentProps).not.toBeNull());
3519
-
3520
- // smsTemplateHasMustacheTags returns false for DLT-only → extractedTags is []
3521
- expect(lastLeftPanelContentProps.extractedTags).toEqual([]);
3522
- });
3523
-
3524
- it('should build synthetic tags from {{mustache}} tokens, excluding DLT {#var#} tokens', async () => {
3525
- render(
3526
- <TestWrapper>
3527
- <CommonTestAndPreview
3528
- {...defaultProps}
3529
- channel={CHANNELS.SMS}
3530
- formData={{ 0: { 'sms-editor': 'Hi {{name}}, order {#orderId#} shipped' } }}
3531
- extractedTags={[]}
3532
- />
3533
- </TestWrapper>
3534
- );
3535
-
3536
- await waitFor(() => expect(lastLeftPanelContentProps).not.toBeNull());
3537
-
3538
- const tags = lastLeftPanelContentProps.extractedTags;
3539
- // Only {{name}} should become a tag — {#orderId#} must be excluded
3540
- expect(tags).toHaveLength(1);
3541
- expect(tags[0].name).toBe('name');
3542
- });
3543
-
3544
- it('should use API-extracted tags when present instead of building synthetic ones', async () => {
3545
- const apiTags = [{ name: 'firstName', metaData: { userDriven: true }, children: [] }];
3546
- render(
3547
- <TestWrapper>
3548
- <CommonTestAndPreview
3549
- {...defaultProps}
3550
- channel={CHANNELS.SMS}
3551
- formData={{ 0: { 'sms-editor': 'Hi {{firstName}}' } }}
3552
- extractedTags={apiTags}
3553
- />
3554
- </TestWrapper>
3555
- );
3556
-
3557
- await waitFor(() => expect(lastLeftPanelContentProps).not.toBeNull());
3558
-
3559
- // API tags take priority over buildSyntheticSmsMustacheTags
3560
- expect(lastLeftPanelContentProps.extractedTags).toEqual(apiTags);
3561
- });
3562
- });
3563
-
3564
- describe('handleDiscardCustomValues — preview reset', () => {
3565
- it('should call updatePreviewRequested when handleDiscardCustomValues is invoked', async () => {
3566
- render(
3567
- <TestWrapper>
3568
- <CommonTestAndPreview
3569
- {...defaultProps}
3570
- channel={CHANNELS.SMS}
3571
- formData={{ 0: { 'sms-editor': 'Hi {{name}}' } }}
3572
- />
3573
- </TestWrapper>
3574
- );
3575
-
3576
- await waitFor(() => expect(lastCustomValuesEditorProps).not.toBeNull());
3577
-
3578
- lastCustomValuesEditorProps.handleDiscardCustomValues();
3579
-
3580
- expect(mockActions.updatePreviewRequested).toHaveBeenCalledTimes(1);
3581
- });
3582
-
3583
- it('should call updatePreviewRequested with RCS channel when handleDiscardCustomValues is invoked for RCS with fallback', async () => {
3584
- render(
3585
- <TestWrapper>
3586
- <CommonTestAndPreview
3587
- {...defaultProps}
3588
- channel={CHANNELS.RCS}
3589
- smsFallbackContent={{
3590
- templateContent: 'Fallback {{name}}',
3591
- content: 'Fallback {{name}}',
3592
- }}
3593
- formData={{}}
3594
- />
3595
- </TestWrapper>
3596
- );
3597
-
3598
- await waitFor(() => expect(lastCustomValuesEditorProps).not.toBeNull());
3599
-
3600
- lastCustomValuesEditorProps.handleDiscardCustomValues();
3601
-
3602
- // updatePreviewRequested is called; syncSmsFallbackPreview (which hits Api directly) is NOT called
3603
- expect(mockActions.updatePreviewRequested).toHaveBeenCalledTimes(1);
3604
- });
3605
- });
3606
3478
  });
3607
-
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import {
8
- call, put, takeLatest, takeEvery, all, take, fork, cancel,
8
+ call, put, takeLatest, all,
9
9
  } from 'redux-saga/effects';
10
10
  import {
11
11
  searchCustomersSaga,
@@ -1333,26 +1333,9 @@ 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
- take('app/CommonTestAndPreview/GET_SENDER_DETAILS_REQUESTED')
1337
- );
1338
- const firstAction = {
1339
- payload: { channel: 'RCS', orgUnitId: 10 },
1340
- };
1341
- expect(generator.next(firstAction).value).toEqual(
1342
- fork(getSenderDetailsSaga, firstAction)
1343
- );
1344
- expect(generator.next({ '@@redux-saga/TASK': true }).value).toEqual(
1345
- take('app/CommonTestAndPreview/GET_SENDER_DETAILS_REQUESTED')
1346
- );
1347
- const secondAction = {
1348
- payload: { channel: 'RCS', orgUnitId: 20 },
1349
- };
1350
- expect(generator.next(secondAction).value).toEqual(
1351
- cancel({ '@@redux-saga/TASK': true })
1352
- );
1353
- expect(generator.next().value).toEqual(
1354
- fork(getSenderDetailsSaga, secondAction)
1336
+ takeLatest('app/CommonTestAndPreview/GET_SENDER_DETAILS_REQUESTED', getSenderDetailsSaga)
1355
1337
  );
1338
+ expect(generator.next().done).toBe(true);
1356
1339
  });
1357
1340
 
1358
1341
  it('should watch for GET_WECRM_ACCOUNTS_REQUESTED', () => {
@@ -1471,24 +1454,34 @@ describe('CommonTestAndPreview Sagas', () => {
1471
1454
  const action = { payload: {} };
1472
1455
  const generator = getWeCrmAccountsSaga(action);
1473
1456
  generator.next();
1474
- const throwResult = generator.throw(new Error('API failed'));
1475
- expect(throwResult.value).toEqual(
1457
+ const err = generator.throw(new Error('API failed'));
1458
+ expect(err.value).toEqual(
1476
1459
  put({ type: GET_WECRM_ACCOUNTS_FAILURE, payload: { error: 'API failed' } })
1477
1460
  );
1478
1461
  expect(generator.next().done).toBe(true);
1479
1462
  });
1480
1463
  });
1481
1464
 
1482
- it('should initialize all watcher sagas', () => {
1483
- const generator = commonTestAndPreviewSaga();
1465
+ describe('commonTestAndPreviewSaga', () => {
1466
+ it('should fork all watcher sagas', () => {
1467
+ const generator = commonTestAndPreviewSaga();
1484
1468
 
1485
- const effect = generator.next().value;
1469
+ expect(generator.next().value).toEqual(
1470
+ all([
1471
+ watchSearchCustomers(),
1472
+ watchExtractTags(),
1473
+ watchUpdatePreview(),
1474
+ watchSendTestMessage(),
1475
+ watchFetchTestCustomers(),
1476
+ watchFetchTestGroups(),
1477
+ watchCreateMessageMeta(),
1478
+ watchGetPrefilledValues(),
1479
+ watchGetSenderDetails(),
1480
+ watchGetWeCrmAccounts(),
1481
+ ])
1482
+ );
1486
1483
 
1487
- // redux-saga v0.x stores the array under effect[ALL] key; v1.x uses effect.payload
1488
- const effectData = effect?.payload || effect?.ALL;
1489
- const watchers = Array.isArray(effectData) ? effectData : Object.values(effectData || {});
1490
- expect(Array.isArray(watchers)).toBe(true);
1491
- expect(watchers.length).toBeGreaterThan(0);
1492
- expect(generator.next().done).toBe(true);
1484
+ expect(generator.next().done).toBe(true);
1485
+ });
1493
1486
  });
1494
1487
  });
@@ -12,8 +12,10 @@ import { loadItem } from 'services/localStorageApi';
12
12
  import { intlShape, injectIntl } from 'react-intl';
13
13
  import { withRouter } from 'react-router-dom';
14
14
  import { get } from 'lodash';
15
- import CapNavigation from '@capillarytech/cap-ui-library/CapNavigation';
15
+ import CapNavigation from '@capillarytech/cap-ui-library/CapNavigationSPA';
16
16
  import { CAP_WHITE } from '@capillarytech/cap-ui-library/styled/variables';
17
+ import { MFEModuleHeader } from '@capillarytech/cap-ui-utils';
18
+ import { MFE_MODULE_HEADER } from './mfeModuleHeader.config';
17
19
  import injectSaga from '../../utils/injectSaga';
18
20
  import sagas from './saga';
19
21
  import messages from './messages';
@@ -30,12 +32,11 @@ import { CapLeftNavigatioOpenCss, CapLeftNavigationCss } from './style';
30
32
  import { EMBEDDED } from './constants';
31
33
 
32
34
  const CapWrapper = styled.div`
33
- position: absolute;
34
35
  padding: 0;
35
36
  background-color: ${CAP_WHITE};
36
37
  width: 100%;
37
38
  display: flex;
38
- margin-top: ${(props) => props.isEmbedded ? '0px' : '64px'};
39
+
39
40
 
40
41
  .sidebar-container {
41
42
  margin-right: 10px;
@@ -46,10 +47,8 @@ const CapWrapper = styled.div`
46
47
  `;
47
48
 
48
49
  const ComponentWrapper = styled.div`
49
- max-width: 1140px;
50
- margin: 0 auto;
51
50
  width: 100%;
52
- padding: 24px 0;
51
+ padding: 0;
53
52
  `;
54
53
 
55
54
  export class NavigationBar extends React.Component {
@@ -58,6 +57,7 @@ export class NavigationBar extends React.Component {
58
57
  this.state = this.initializeSelectedProduct();
59
58
  }
60
59
 
60
+
61
61
  initializeSelectedProduct = () => {
62
62
  const { location, intl: { formatMessage } } = this.props;
63
63
  const { pathname } = location;
@@ -179,6 +179,7 @@ export class NavigationBar extends React.Component {
179
179
  const topbarIcons = this.getTopbarIcons(showDocumentationBot);
180
180
  const headerOverideCss = leftNavbarExpandedProp ? CapLeftNavigatioOpenCss : CapLeftNavigationCss;
181
181
  return (
182
+ <>
182
183
  <CapNavigation
183
184
  className="creatives-main-container"
184
185
  showContent
@@ -212,7 +213,8 @@ export class NavigationBar extends React.Component {
212
213
  </CapWrapper>
213
214
  </div>
214
215
  </CapNavigation>
215
-
216
+ <MFEModuleHeader history={this.props.history} {...MFE_MODULE_HEADER} />
217
+ </>
216
218
  );
217
219
  }
218
220
  }
@@ -0,0 +1,16 @@
1
+ // Static, module-specific config for the shared MFE module-header wrapper
2
+ // (MFEModuleHeader). Runtime values (history) are supplied in NavigationBar.
3
+ export const MFE_MODULE_HEADER = {
4
+ header: {
5
+ name: 'Creatives',
6
+ description: 'Store your creative templates for all your channels',
7
+ showBorder: false,
8
+ showSettings: false,
9
+ },
10
+ landingRoutes: [
11
+ '/creatives/ui/v2/',
12
+ '/creatives/ui/v2',
13
+ '/v2/',
14
+ '/v2'
15
+ ],
16
+ };
@@ -2,6 +2,8 @@
2
2
  height: calc(100vh - 20rem);
3
3
  overflow-y: auto;
4
4
  overflow-x: hidden;
5
+ margin-right: -24px;
6
+ padding-right: 24px;
5
7
  }
6
8
 
7
9
  .v2-pagination-container.full-height {
@@ -13,7 +13,9 @@
13
13
 
14
14
  .shell-v2 {
15
15
  position: relative;
16
- margin: 0 auto;
16
+ -webkit-transform: translate(-50%);
17
+ transform: translate(-50%);
18
+ left: 50%;
17
19
  padding: 0 10px;
18
20
  width: 450px;
19
21
 
@@ -535,21 +537,20 @@
535
537
  .unicode-disabled{
536
538
  font-size: 16px;
537
539
  }
538
- position: absolute;
539
- overflow: auto;
540
- top: 0;
541
- left: 17%;
542
- white-space: pre-wrap;
543
- word-break: break-word;
544
- max-height: 100%;
545
- line-height: 0.875rem;
546
- font-size: 0.625rem;
547
- font-family: 'open-sans';
548
-
549
540
  &.sms {
541
+ max-height: 100%;
542
+ position: absolute;
550
543
  // width: initial;
544
+ overflow: auto;
545
+ top: 0;
546
+ left: 17%;
547
+ white-space: pre-wrap;
548
+ word-break: break-word;
549
+ line-height: 14px;
551
550
  padding: 0 8px 8px 8px;
551
+ font-size: 10px;
552
552
  width: 100%;
553
+ font-family: 'open-sans';
553
554
  .rcs-image{
554
555
  width: 100%;
555
556
  height: 123px;
@@ -570,9 +571,6 @@
570
571
  margin-top: 0;
571
572
  padding: 4px 8px;
572
573
  }
573
- &.rcs-desc {
574
- margin-top: $CAP_SPACE_08;
575
- }
576
574
  &.rcs-button-text{
577
575
  color: $FONT_COLOR_05;
578
576
  .rcs-icon{
@@ -584,12 +582,22 @@
584
582
  &:not(.sms){
585
583
  padding: 4px;
586
584
  background: #3F51B5;
585
+ position: absolute;
587
586
  // width: initial;
587
+ overflow: auto;
588
+ top: 0;
589
+ left: 17%;
590
+ white-space: pre-wrap;
591
+ word-break: break-word;
588
592
  border-radius: 4px;
593
+ max-height: 100%;
589
594
  color: #FFFFFF;
590
595
  width: 70%;
591
596
  min-height: 26px;
597
+ line-height: 14px;
592
598
  padding: 8px;
599
+ font-size: 10px;
600
+ font-family: 'open-sans';
593
601
  }
594
602
  &.message-pop-carousel {
595
603
  position: relative;
@@ -834,13 +842,9 @@
834
842
 
835
843
  .msg-container-carousel {
836
844
  display: flex;
837
- width: 100%;
838
845
  .scroll-container {
839
846
  overflow-x: auto;
840
- overflow-y: hidden;
841
847
  display: flex;
842
- flex-wrap: nowrap;
843
- width: 100%;
844
848
  padding-top: $CAP_SPACE_06;
845
849
  padding-right: $CAP_SPACE_06;
846
850
  white-space: nowrap;
@@ -861,32 +865,6 @@
861
865
  }
862
866
  }
863
867
 
864
- .shell-v2.rcs-preview {
865
- // Collapse the default 8px margin so the divider renders as a clean hairline, not a white gap.
866
- .whatsapp-divider {
867
- margin: 0;
868
- }
869
-
870
- // Override `.message-pop:not(.sms)` (blue background) for RCS carousel cards.
871
- .msg-container.sms {
872
- .message-pop.sms {
873
- .rcs-carousel-card {
874
- background: $CAP_WHITE;
875
- color: $CAP_G01;
876
- width: 10.4rem;
877
- left: 0;
878
- flex-shrink: 0;
879
- padding: $CAP_SPACE_04 0 $CAP_SPACE_08;
880
- border-radius: $CAP_SPACE_04; // 4px (Figma RCS carousel card corner)
881
-
882
- .carousel-title {
883
- font-weight: 700 !important;
884
- }
885
- }
886
- }
887
- }
888
- }
889
-
890
868
  .align-center {
891
869
  text-align: center;
892
870
  }
@@ -1085,6 +1063,8 @@
1085
1063
  }
1086
1064
  .video-icon {
1087
1065
  position: absolute;
1066
+ right: -17px;
1067
+ bottom: -17px;
1088
1068
  }
1089
1069
 
1090
1070
  .zalo-preview-container {