@capillarytech/creatives-library 8.0.292-alpha.0 → 8.0.293

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 (80) hide show
  1. package/constants/unified.js +1 -3
  2. package/initialState.js +2 -0
  3. package/package.json +1 -1
  4. package/utils/common.js +8 -5
  5. package/utils/commonUtils.js +85 -4
  6. package/utils/tagValidations.js +223 -83
  7. package/utils/tests/commonUtil.test.js +124 -147
  8. package/utils/tests/tagValidations.test.js +358 -441
  9. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +33 -0
  10. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +422 -0
  11. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.scss +35 -0
  12. package/v2Components/CommonTestAndPreview/DeliverySettings/TECH_DETAILING_DELIVERY_SETTINGS.md +725 -0
  13. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +92 -0
  14. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +251 -0
  15. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +111 -0
  16. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +91 -0
  17. package/v2Components/CommonTestAndPreview/SendTestMessage.js +51 -1
  18. package/v2Components/CommonTestAndPreview/actions.js +20 -0
  19. package/v2Components/CommonTestAndPreview/constants.js +10 -0
  20. package/v2Components/CommonTestAndPreview/index.js +148 -15
  21. package/v2Components/CommonTestAndPreview/reducer.js +47 -0
  22. package/v2Components/CommonTestAndPreview/sagas.js +60 -0
  23. package/v2Components/CommonTestAndPreview/selectors.js +51 -0
  24. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +889 -0
  25. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +222 -0
  26. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +235 -0
  27. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +135 -0
  28. package/v2Components/CommonTestAndPreview/tests/actions.test.js +50 -0
  29. package/v2Components/CommonTestAndPreview/tests/constants.test.js +18 -0
  30. package/v2Components/CommonTestAndPreview/tests/index.test.js +342 -1
  31. package/v2Components/CommonTestAndPreview/tests/reducer.test.js +118 -0
  32. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +145 -0
  33. package/v2Components/CommonTestAndPreview/tests/selectors.test.js +146 -0
  34. package/v2Components/ErrorInfoNote/index.js +5 -2
  35. package/v2Components/FormBuilder/index.js +203 -137
  36. package/v2Components/FormBuilder/messages.js +8 -0
  37. package/v2Components/HtmlEditor/HTMLEditor.js +5 -0
  38. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -0
  39. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +15 -0
  40. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +2 -1
  41. package/v2Components/TestAndPreviewSlidebox/index.js +14 -0
  42. package/v2Containers/Cap/mockData.js +14 -0
  43. package/v2Containers/Cap/reducer.js +55 -3
  44. package/v2Containers/Cap/tests/reducer.test.js +102 -0
  45. package/v2Containers/CreativesContainer/SlideBoxContent.js +1 -5
  46. package/v2Containers/CreativesContainer/SlideBoxFooter.js +5 -13
  47. package/v2Containers/CreativesContainer/index.js +7 -30
  48. package/v2Containers/Email/index.js +5 -1
  49. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +70 -23
  50. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +137 -29
  51. package/v2Containers/FTP/index.js +51 -2
  52. package/v2Containers/FTP/messages.js +4 -0
  53. package/v2Containers/InApp/index.js +104 -4
  54. package/v2Containers/InApp/tests/index.test.js +6 -17
  55. package/v2Containers/InappAdvance/index.js +108 -4
  56. package/v2Containers/InappAdvance/tests/index.test.js +0 -2
  57. package/v2Containers/Line/Container/Text/index.js +1 -0
  58. package/v2Containers/MobilePush/Create/index.js +19 -42
  59. package/v2Containers/MobilePush/Edit/index.js +19 -42
  60. package/v2Containers/MobilePushNew/index.js +32 -12
  61. package/v2Containers/MobilepushWrapper/index.js +1 -3
  62. package/v2Containers/Rcs/index.js +37 -12
  63. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +697 -12
  64. package/v2Containers/Sms/Create/index.js +3 -39
  65. package/v2Containers/Sms/Create/messages.js +0 -4
  66. package/v2Containers/Sms/Edit/index.js +3 -35
  67. package/v2Containers/Sms/commonMethods.js +6 -3
  68. package/v2Containers/SmsTrai/Edit/index.js +52 -12
  69. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +207 -6
  70. package/v2Containers/SmsWrapper/index.js +0 -2
  71. package/v2Containers/Viber/index.js +1 -0
  72. package/v2Containers/WebPush/Create/hooks/useTagManagement.js +3 -1
  73. package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +7 -0
  74. package/v2Containers/WebPush/Create/index.js +2 -2
  75. package/v2Containers/WebPush/Create/utils/validation.js +2 -17
  76. package/v2Containers/WebPush/Create/utils/validation.test.js +24 -59
  77. package/v2Containers/Whatsapp/index.js +18 -10
  78. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +26242 -4225
  79. package/v2Containers/Zalo/index.js +11 -3
  80. package/v2Containers/Sms/tests/commonMethods.test.js +0 -122
@@ -25,9 +25,17 @@ import {
25
25
  watchFetchTestGroups,
26
26
  watchCreateMessageMeta,
27
27
  watchGetPrefilledValues,
28
+ getSenderDetailsSaga,
29
+ getWeCrmAccountsSaga,
30
+ watchGetSenderDetails,
31
+ watchGetWeCrmAccounts,
28
32
  commonTestAndPreviewSaga,
29
33
  } from '../sagas';
30
34
  import * as Api from '../../../services/api';
35
+ jest.mock('../DeliverySettings/utils/parseSenderDetailsResponse', () => ({
36
+ parseSenderDetailsResponse: jest.fn((channel, response) => ({ domains: response?.entity?.[channel] || [] })),
37
+ }));
38
+ import { parseSenderDetailsResponse } from '../DeliverySettings/utils/parseSenderDetailsResponse';
31
39
  import {
32
40
  SEARCH_CUSTOMERS_SUCCESS,
33
41
  SEARCH_CUSTOMERS_FAILURE,
@@ -45,6 +53,10 @@ import {
45
53
  CREATE_MESSAGE_META_FAILURE,
46
54
  GET_PREFILLED_VALUES_SUCCESS,
47
55
  GET_PREFILLED_VALUES_FAILURE,
56
+ GET_SENDER_DETAILS_SUCCESS,
57
+ GET_SENDER_DETAILS_FAILURE,
58
+ GET_WECRM_ACCOUNTS_SUCCESS,
59
+ GET_WECRM_ACCOUNTS_FAILURE,
48
60
  } from '../constants';
49
61
 
50
62
  describe('CommonTestAndPreview Sagas', () => {
@@ -1694,6 +1706,137 @@ describe('CommonTestAndPreview Sagas', () => {
1694
1706
  );
1695
1707
  expect(generator.next().done).toBe(true);
1696
1708
  });
1709
+
1710
+ it('should watch for GET_SENDER_DETAILS_REQUESTED', () => {
1711
+ const generator = watchGetSenderDetails();
1712
+ expect(generator.next().value).toEqual(
1713
+ takeLatest('app/CommonTestAndPreview/GET_SENDER_DETAILS_REQUESTED', getSenderDetailsSaga)
1714
+ );
1715
+ expect(generator.next().done).toBe(true);
1716
+ });
1717
+
1718
+ it('should watch for GET_WECRM_ACCOUNTS_REQUESTED', () => {
1719
+ const generator = watchGetWeCrmAccounts();
1720
+ expect(generator.next().value).toEqual(
1721
+ takeLatest('app/CommonTestAndPreview/GET_WECRM_ACCOUNTS_REQUESTED', getWeCrmAccountsSaga)
1722
+ );
1723
+ expect(generator.next().done).toBe(true);
1724
+ });
1725
+ });
1726
+
1727
+ describe('getSenderDetailsSaga', () => {
1728
+ it('should fetch sender details and put SUCCESS with parsed domains', () => {
1729
+ const action = { payload: { channel: 'SMS', orgUnitId: 100 } };
1730
+ const apiResponse = { entity: { SMS: [{ id: 1, domainProperties: {} }] } };
1731
+ parseSenderDetailsResponse.mockReturnValue({ domains: [{ domainId: 1 }] });
1732
+
1733
+ const generator = getSenderDetailsSaga(action);
1734
+
1735
+ expect(generator.next().value).toEqual(call(Api.getSenderDetails, 'SMS', 100));
1736
+ expect(generator.next(apiResponse).value).toEqual(
1737
+ put({ type: GET_SENDER_DETAILS_SUCCESS, payload: { channel: 'SMS', domains: [{ domainId: 1 }] } })
1738
+ );
1739
+ expect(generator.next().done).toBe(true);
1740
+ });
1741
+
1742
+ it('should use orgUnitId -1 when not provided', () => {
1743
+ const action = { payload: { channel: 'EMAIL' } };
1744
+ const apiResponse = { entity: { EMAIL: [] } };
1745
+ parseSenderDetailsResponse.mockReturnValue({ domains: [] });
1746
+
1747
+ const generator = getSenderDetailsSaga(action);
1748
+
1749
+ expect(generator.next().value).toEqual(call(Api.getSenderDetails, 'EMAIL', -1));
1750
+ generator.next(apiResponse);
1751
+ expect(generator.next().done).toBe(true);
1752
+ });
1753
+
1754
+ it('should return early when channel is missing', () => {
1755
+ const action = { payload: {} };
1756
+ const generator = getSenderDetailsSaga(action);
1757
+ expect(generator.next().done).toBe(true);
1758
+ });
1759
+
1760
+ it('should put FAILURE when response has errors', () => {
1761
+ const action = { payload: { channel: 'SMS' } };
1762
+ const apiResponse = { errors: [{ message: 'Forbidden' }] };
1763
+
1764
+ const generator = getSenderDetailsSaga(action);
1765
+ generator.next();
1766
+ expect(generator.next(apiResponse).value).toEqual(
1767
+ put({
1768
+ type: GET_SENDER_DETAILS_FAILURE,
1769
+ payload: { channel: 'SMS', error: [{ message: 'Forbidden' }] },
1770
+ })
1771
+ );
1772
+ expect(generator.next().done).toBe(true);
1773
+ });
1774
+
1775
+ it('should put FAILURE on throw', () => {
1776
+ const action = { payload: { channel: 'WHATSAPP' } };
1777
+ const generator = getSenderDetailsSaga(action);
1778
+ generator.next();
1779
+ const err = generator.throw(new Error('Network error'));
1780
+ expect(err.value).toEqual(
1781
+ put({
1782
+ type: GET_SENDER_DETAILS_FAILURE,
1783
+ payload: { channel: 'WHATSAPP', error: 'Network error' },
1784
+ })
1785
+ );
1786
+ expect(generator.next().done).toBe(true);
1787
+ });
1788
+ });
1789
+
1790
+ describe('getWeCrmAccountsSaga', () => {
1791
+ it('should fetch wecrm accounts and put SUCCESS with list from response', () => {
1792
+ const action = { payload: { sourceName: 'WHATSAPP' } };
1793
+ const apiResult = { response: [{ name: 'WABA1', sourceAccountIdentifier: 'waba-1' }] };
1794
+
1795
+ const generator = getWeCrmAccountsSaga(action);
1796
+
1797
+ expect(generator.next().value).toEqual(call(Api.fetchWeCrmAccounts, 'WHATSAPP'));
1798
+ expect(generator.next(apiResult).value).toEqual(
1799
+ put({ type: GET_WECRM_ACCOUNTS_SUCCESS, payload: { accounts: [{ name: 'WABA1', sourceAccountIdentifier: 'waba-1' }] } })
1800
+ );
1801
+ expect(generator.next().done).toBe(true);
1802
+ });
1803
+
1804
+ it('should use WHATSAPP when sourceName not provided', () => {
1805
+ const action = { payload: {} };
1806
+ const generator = getWeCrmAccountsSaga(action);
1807
+ expect(generator.next().value).toEqual(call(Api.fetchWeCrmAccounts, 'WHATSAPP'));
1808
+ });
1809
+
1810
+ it('should use entity when response not present', () => {
1811
+ const action = { payload: {} };
1812
+ const apiResult = { entity: [{ name: 'Acc1' }] };
1813
+
1814
+ const generator = getWeCrmAccountsSaga(action);
1815
+ generator.next();
1816
+ expect(generator.next(apiResult).value).toEqual(
1817
+ put({ type: GET_WECRM_ACCOUNTS_SUCCESS, payload: { accounts: [{ name: 'Acc1' }] } })
1818
+ );
1819
+ });
1820
+
1821
+ it('should put empty array when result has no list', () => {
1822
+ const action = { payload: {} };
1823
+ const generator = getWeCrmAccountsSaga(action);
1824
+ generator.next();
1825
+ expect(generator.next({}).value).toEqual(
1826
+ put({ type: GET_WECRM_ACCOUNTS_SUCCESS, payload: { accounts: [] } })
1827
+ );
1828
+ });
1829
+
1830
+ it('should put FAILURE on throw', () => {
1831
+ const action = { payload: {} };
1832
+ const generator = getWeCrmAccountsSaga(action);
1833
+ generator.next();
1834
+ const err = generator.throw(new Error('API failed'));
1835
+ expect(err.value).toEqual(
1836
+ put({ type: GET_WECRM_ACCOUNTS_FAILURE, payload: { error: 'API failed' } })
1837
+ );
1838
+ expect(generator.next().done).toBe(true);
1839
+ });
1697
1840
  });
1698
1841
 
1699
1842
  describe('commonTestAndPreviewSaga', () => {
@@ -1710,6 +1853,8 @@ describe('CommonTestAndPreview Sagas', () => {
1710
1853
  watchFetchTestGroups(),
1711
1854
  watchCreateMessageMeta(),
1712
1855
  watchGetPrefilledValues(),
1856
+ watchGetSenderDetails(),
1857
+ watchGetWeCrmAccounts(),
1713
1858
  ])
1714
1859
  );
1715
1860
 
@@ -32,6 +32,12 @@ import {
32
32
  makeSelectUpdatePreviewErrors,
33
33
  makeSelectFetchPrefilledValuesError,
34
34
  makeSelectFetchPrefilledValuesErrors,
35
+ makeSelectSenderDetailsForChannel,
36
+ makeSelectSenderDetailsByChannel,
37
+ makeSelectWeCrmAccounts,
38
+ makeSelectIsLoadingSenderDetails,
39
+ makeSelectFetchSenderDetailsError,
40
+ makeSelectFetchWeCrmAccountsError,
35
41
  } from '../selectors';
36
42
 
37
43
  describe('CommonTestAndPreview Selectors', () => {
@@ -101,6 +107,19 @@ describe('CommonTestAndPreview Selectors', () => {
101
107
  fetchPrefilledValuesErrors: [
102
108
  { code: 7078, message: 'User profile not found' },
103
109
  ],
110
+ senderDetailsByChannel: {
111
+ SMS: [
112
+ { domainId: 1, domainName: 'SMS Domain', gsmSenders: [{ value: 'sender1' }] },
113
+ ],
114
+ EMAIL: [],
115
+ WHATSAPP: [],
116
+ },
117
+ wecrmAccounts: [
118
+ { name: 'WeCRM Account 1', sourceAccountIdentifier: 'waba-1' },
119
+ ],
120
+ isLoadingSenderDetails: false,
121
+ fetchSenderDetailsError: null,
122
+ fetchWeCrmAccountsError: null,
104
123
  },
105
124
  });
106
125
 
@@ -550,6 +569,133 @@ describe('CommonTestAndPreview Selectors', () => {
550
569
  });
551
570
  });
552
571
 
572
+ describe('makeSelectSenderDetailsForChannel', () => {
573
+ it('should select sender details for given channel as JS array', () => {
574
+ const selector = makeSelectSenderDetailsForChannel('SMS');
575
+ const result = selector(mockState);
576
+
577
+ expect(result).toBeDefined();
578
+ expect(Array.isArray(result)).toBe(true);
579
+ expect(result.length).toBe(1);
580
+ expect(result[0].domainName).toBe('SMS Domain');
581
+ });
582
+
583
+ it('should return empty array for channel with no data', () => {
584
+ const selector = makeSelectSenderDetailsForChannel('EMAIL');
585
+ const result = selector(mockState);
586
+
587
+ expect(result).toEqual([]);
588
+ });
589
+
590
+ it('should return empty array when substate or byChannel missing', () => {
591
+ const emptyState = fromJS({});
592
+ const selector = makeSelectSenderDetailsForChannel('SMS');
593
+ const result = selector(emptyState);
594
+
595
+ expect(result).toEqual([]);
596
+ });
597
+ });
598
+
599
+ describe('makeSelectSenderDetailsByChannel', () => {
600
+ it('should select senderDetailsByChannel and convert to JS object', () => {
601
+ const selector = makeSelectSenderDetailsByChannel();
602
+ const result = selector(mockState);
603
+
604
+ expect(result).toBeDefined();
605
+ expect(result.SMS).toBeDefined();
606
+ expect(Array.isArray(result.SMS)).toBe(true);
607
+ expect(result.SMS[0].domainName).toBe('SMS Domain');
608
+ expect(result.EMAIL).toEqual([]);
609
+ expect(result.WHATSAPP).toEqual([]);
610
+ });
611
+
612
+ it('should return empty object when domain is missing', () => {
613
+ const emptyState = fromJS({});
614
+ const selector = makeSelectSenderDetailsByChannel();
615
+ const result = selector(emptyState);
616
+
617
+ expect(result).toEqual({});
618
+ });
619
+ });
620
+
621
+ describe('makeSelectWeCrmAccounts', () => {
622
+ it('should select wecrmAccounts and convert to JS array', () => {
623
+ const selector = makeSelectWeCrmAccounts();
624
+ const result = selector(mockState);
625
+
626
+ expect(result).toBeDefined();
627
+ expect(Array.isArray(result)).toBe(true);
628
+ expect(result.length).toBe(1);
629
+ expect(result[0].name).toBe('WeCRM Account 1');
630
+ expect(result[0].sourceAccountIdentifier).toBe('waba-1');
631
+ });
632
+
633
+ it('should return empty array when wecrmAccounts is missing', () => {
634
+ const emptyState = fromJS({ commonTestAndPreview: {} });
635
+ const selector = makeSelectWeCrmAccounts();
636
+ const result = selector(emptyState);
637
+
638
+ expect(result).toEqual([]);
639
+ });
640
+ });
641
+
642
+ describe('makeSelectIsLoadingSenderDetails', () => {
643
+ it('should select isLoadingSenderDetails flag', () => {
644
+ const selector = makeSelectIsLoadingSenderDetails();
645
+ const result = selector(mockState);
646
+
647
+ expect(result).toBe(false);
648
+ });
649
+
650
+ it('should return true when loading', () => {
651
+ const loadingState = fromJS({
652
+ commonTestAndPreview: { isLoadingSenderDetails: true },
653
+ });
654
+ const selector = makeSelectIsLoadingSenderDetails();
655
+ const result = selector(loadingState);
656
+
657
+ expect(result).toBe(true);
658
+ });
659
+ });
660
+
661
+ describe('makeSelectFetchSenderDetailsError', () => {
662
+ it('should select fetchSenderDetailsError from state', () => {
663
+ const selector = makeSelectFetchSenderDetailsError();
664
+ const result = selector(mockState);
665
+
666
+ expect(result).toBeNull();
667
+ });
668
+
669
+ it('should return error when present', () => {
670
+ const errorState = fromJS({
671
+ commonTestAndPreview: { fetchSenderDetailsError: 'Sender fetch failed' },
672
+ });
673
+ const selector = makeSelectFetchSenderDetailsError();
674
+ const result = selector(errorState);
675
+
676
+ expect(result).toBe('Sender fetch failed');
677
+ });
678
+ });
679
+
680
+ describe('makeSelectFetchWeCrmAccountsError', () => {
681
+ it('should select fetchWeCrmAccountsError from state', () => {
682
+ const selector = makeSelectFetchWeCrmAccountsError();
683
+ const result = selector(mockState);
684
+
685
+ expect(result).toBeNull();
686
+ });
687
+
688
+ it('should return error when present', () => {
689
+ const errorState = fromJS({
690
+ commonTestAndPreview: { fetchWeCrmAccountsError: 'WeCRM fetch failed' },
691
+ });
692
+ const selector = makeSelectFetchWeCrmAccountsError();
693
+ const result = selector(errorState);
694
+
695
+ expect(result).toBe('WeCRM fetch failed');
696
+ });
697
+ });
698
+
553
699
  describe('Edge Cases', () => {
554
700
  it('should handle undefined state gracefully', () => {
555
701
  const selector = makeSelectCustomers();
@@ -186,6 +186,7 @@ export const ErrorInfoNote = (props) => {
186
186
  errorMessages,
187
187
  onErrorClick,
188
188
  onClose,
189
+ isLiquidEnabled = true,
189
190
  intl,
190
191
  useLegacyDisplay = false, // Use simple list display instead of tabs (for BEE Editor)
191
192
  } = props;
@@ -229,7 +230,7 @@ export const ErrorInfoNote = (props) => {
229
230
  const standardErrors = Array.isArray(rawStandardErrors) ? rawStandardErrors : [];
230
231
  const liquidErrors = Array.isArray(rawLiquidErrors) ? rawLiquidErrors : [];
231
232
  const hasStandardErrors = standardErrors.length > 0;
232
- const hasLiquidErrors = liquidErrors.length > 0;
233
+ const hasLiquidErrors = liquidErrors.length > 0 && isLiquidEnabled;
233
234
 
234
235
  if (!hasStandardErrors && !hasLiquidErrors) {
235
236
  return null;
@@ -356,7 +357,7 @@ export const ErrorInfoNote = (props) => {
356
357
  className="error-info-note__tabs"
357
358
  />
358
359
  <CapRow className="error-info-note__actions">
359
- {hasLiquidErrors && (
360
+ {hasLiquidErrors && isLiquidEnabled && (
360
361
  <CapButton
361
362
  type="flat"
362
363
  className="error-info-note__liquid-doc"
@@ -451,6 +452,7 @@ ErrorInfoNote.defaultProps = {
451
452
  },
452
453
  onErrorClick: null,
453
454
  onClose: null,
455
+ isLiquidEnabled: true,
454
456
  intl: null,
455
457
  useLegacyDisplay: false, // Use simple list display for BEE Editor
456
458
  };
@@ -477,6 +479,7 @@ ErrorInfoNote.propTypes = {
477
479
  }),
478
480
  onErrorClick: PropTypes.func,
479
481
  onClose: PropTypes.func,
482
+ isLiquidEnabled: PropTypes.bool,
480
483
  intl: PropTypes.object,
481
484
  useLegacyDisplay: PropTypes.bool, // Use simple list display for BEE Editor
482
485
  };