@capillarytech/creatives-library 8.0.340-beta.0.4 → 8.0.340-beta.0.6

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 (67) hide show
  1. package/constants/unified.js +1 -0
  2. package/package.json +1 -1
  3. package/services/api.js +20 -0
  4. package/services/tests/api.test.js +59 -0
  5. package/utils/common.js +6 -0
  6. package/utils/test-utils.js +2 -2
  7. package/utils/tests/v2Common.test.js +46 -1
  8. package/utils/v2common.js +18 -0
  9. package/v2Components/CapTagList/index.js +5 -6
  10. package/v2Components/CapTagListWithInput/index.js +1 -1
  11. package/v2Components/CommonTestAndPreview/UnifiedPreview/WhatsAppPreviewContent.js +18 -6
  12. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +27 -0
  13. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/WhatsAppPreviewContent.test.js +48 -0
  14. package/v2Components/TemplatePreview/_templatePreview.scss +22 -1
  15. package/v2Components/TemplatePreview/index.js +21 -9
  16. package/v2Components/TemplatePreview/tests/__snapshots__/index.test.js.snap +1 -0
  17. package/v2Containers/Assets/images/archive_Empty_Illustration.svg +9 -0
  18. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +28 -20
  19. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +24 -16
  20. package/v2Containers/CreativesContainer/SlideBoxContent.js +16 -5
  21. package/v2Containers/CreativesContainer/SlideBoxFooter.js +3 -1
  22. package/v2Containers/CreativesContainer/SlideBoxHeader.js +4 -4
  23. package/v2Containers/CreativesContainer/index.js +14 -1
  24. package/v2Containers/CreativesContainer/messages.js +4 -0
  25. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +2 -4
  26. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +4 -4
  27. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +3 -0
  28. package/v2Containers/Email/reducer.js +12 -3
  29. package/v2Containers/Email/sagas.js +9 -4
  30. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +4 -0
  31. package/v2Containers/Email/tests/reducer.test.js +47 -0
  32. package/v2Containers/Email/tests/sagas.test.js +146 -6
  33. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +8 -1
  34. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +1 -0
  35. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +7 -0
  36. package/v2Containers/MobilePush/Create/index.js +1 -1
  37. package/v2Containers/MobilePush/Edit/index.js +1 -1
  38. package/v2Containers/Sms/Create/index.js +3 -0
  39. package/v2Containers/Sms/SCHEMA_FORMBUILDER_MAP.md +1 -1
  40. package/v2Containers/Templates/ChannelTypeIllustration.js +23 -6
  41. package/v2Containers/Templates/_templates.scss +155 -24
  42. package/v2Containers/Templates/actions.js +44 -0
  43. package/v2Containers/Templates/constants.js +31 -0
  44. package/v2Containers/Templates/index.js +400 -59
  45. package/v2Containers/Templates/messages.js +96 -0
  46. package/v2Containers/Templates/reducer.js +84 -1
  47. package/v2Containers/Templates/sagas.js +64 -0
  48. package/v2Containers/Templates/selectors.js +12 -0
  49. package/v2Containers/Templates/tests/ChannelTypeIllustration.test.js +12 -0
  50. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1166 -1112
  51. package/v2Containers/Templates/tests/index.test.js +6 -0
  52. package/v2Containers/Templates/tests/reducer.test.js +178 -0
  53. package/v2Containers/Templates/tests/sagas.test.js +390 -8
  54. package/v2Containers/Templates/tests/selector.test.js +32 -0
  55. package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -1
  56. package/v2Containers/Viber/constants.js +8 -0
  57. package/v2Containers/Viber/index.js +5 -0
  58. package/v2Containers/Viber/messages.js +4 -0
  59. package/v2Containers/Viber/reducer.js +26 -3
  60. package/v2Containers/Viber/sagas.js +50 -8
  61. package/v2Containers/Viber/tests/index.test.js +80 -0
  62. package/v2Containers/Viber/tests/reducer.test.js +297 -0
  63. package/v2Containers/Viber/tests/saga.test.js +412 -40
  64. package/v2Containers/Whatsapp/constants.js +8 -0
  65. package/v2Containers/Whatsapp/index.js +145 -5
  66. package/v2Containers/Whatsapp/index.scss +12 -0
  67. package/v2Containers/Whatsapp/messages.js +16 -0
@@ -16,6 +16,53 @@ describe('emailReducer', () => {
16
16
  expect(emailReducer(undefined, action)).toMatchSnapshot();
17
17
  });
18
18
 
19
+ it.concurrent('it handles GET_CMS_EDITOR_DETAILS_SUCCESS action — sets isBeeEnabled true and CmsSettings', () => {
20
+ const initialState = fromJS({ isBeeEnabled: null, fetchingCmsSettings: true });
21
+ const action = {
22
+ type: types.GET_CMS_EDITOR_DETAILS_SUCCESS,
23
+ isBeeEnabled: true,
24
+ settings: { isDragDrop: true, editorType: 'bee' },
25
+ };
26
+ const result = emailReducer(initialState, action);
27
+ expect(result.get('fetchingCmsSettings')).toBe(false);
28
+ expect(result.get('isBeeEnabled')).toBe(true);
29
+ expect(result.getIn(['CmsSettings', 'isDragDrop'])).toBe(true);
30
+ });
31
+
32
+ it.concurrent('it handles GET_CMS_EDITOR_DETAILS_SUCCESS action — sets isBeeEnabled false', () => {
33
+ const initialState = fromJS({ isBeeEnabled: true, fetchingCmsSettings: true });
34
+ const action = {
35
+ type: types.GET_CMS_EDITOR_DETAILS_SUCCESS,
36
+ isBeeEnabled: false,
37
+ settings: {},
38
+ };
39
+ const result = emailReducer(initialState, action);
40
+ expect(result.get('fetchingCmsSettings')).toBe(false);
41
+ expect(result.get('isBeeEnabled')).toBe(false);
42
+ });
43
+
44
+ it.concurrent('it handles GET_CMS_EDITOR_DETAILS_SUCCESS action — overwrites stale isBeeEnabled from CLEAR_ALL_VALUES (null)', () => {
45
+ const initialState = fromJS({ isBeeEnabled: null, fetchingCmsSettings: true });
46
+ const action = {
47
+ type: types.GET_CMS_EDITOR_DETAILS_SUCCESS,
48
+ isBeeEnabled: true,
49
+ settings: { type: 'bee' },
50
+ };
51
+ const result = emailReducer(initialState, action);
52
+ expect(result.get('isBeeEnabled')).toBe(true);
53
+ });
54
+
55
+ it.concurrent('it handles GET_CMS_EDITOR_DETAILS_SUCCESS action — sets CmsSettings to empty object when settings is {}', () => {
56
+ const initialState = fromJS({ CmsSettings: { old: 'data' } });
57
+ const action = {
58
+ type: types.GET_CMS_EDITOR_DETAILS_SUCCESS,
59
+ isBeeEnabled: false,
60
+ settings: {},
61
+ };
62
+ const result = emailReducer(initialState, action);
63
+ expect(result.get('CmsSettings').toJS()).toEqual({});
64
+ });
65
+
19
66
  it.concurrent('it handles GET_CMS_ACCOUNTS_REQUEST action (line 111-113)', () => {
20
67
  const initialState = fromJS({
21
68
  isBeeEnabled: true, // Start with true to verify it gets set to false
@@ -3,6 +3,7 @@ import { expectSaga } from 'redux-saga-test-plan';
3
3
  import { takeLatest } from 'redux-saga/effects';
4
4
  import * as matchers from 'redux-saga-test-plan/matchers';
5
5
  import { throwError } from 'redux-saga-test-plan/providers';
6
+ import { fromJS } from 'immutable';
6
7
  import * as types from '../constants';
7
8
  import * as sagas from '../sagas';
8
9
  import { v2EmailDuplicateTemplateSaga, v2EmailSagas } from '../sagas';
@@ -709,10 +710,14 @@ describe('getCmsSetting saga', () => {
709
710
 
710
711
  return expectSaga(sagas.getCmsSetting, basePayload)
711
712
  .provide([
713
+ {
714
+ select(effect, next) {
715
+ return fromJS({ cmsAccountsLoaded: true, isBeeEnabled: true });
716
+ },
717
+ },
712
718
  [matchers.call.fn(Api.getCmsTemplateSettingsV2), fakeResponse],
713
719
  ])
714
- .put({ type: types.GET_CMS_ACCOUNTS_SUCCESS, isBeeEnabled: true })
715
- .put({ type: types.GET_CMS_EDITOR_DETAILS_SUCCESS, settings: fakeResponse.data.response.cmsDetails })
720
+ .put({ type: types.GET_CMS_EDITOR_DETAILS_SUCCESS, settings: fakeResponse.data.response.cmsDetails, isBeeEnabled: true })
716
721
  .run();
717
722
  });
718
723
 
@@ -727,10 +732,14 @@ describe('getCmsSetting saga', () => {
727
732
 
728
733
  return expectSaga(sagas.getCmsSetting, basePayload)
729
734
  .provide([
735
+ {
736
+ select(effect, next) {
737
+ return fromJS({ cmsAccountsLoaded: true, isBeeEnabled: false });
738
+ },
739
+ },
730
740
  [matchers.call.fn(Api.getCmsTemplateSettingsV2), fakeResponse],
731
741
  ])
732
- .put({ type: types.GET_CMS_ACCOUNTS_SUCCESS, isBeeEnabled: false })
733
- .put({ type: types.GET_CMS_EDITOR_DETAILS_SUCCESS, settings: fakeResponse.data.response.cmsDetails })
742
+ .put({ type: types.GET_CMS_EDITOR_DETAILS_SUCCESS, settings: fakeResponse.data.response.cmsDetails, isBeeEnabled: false })
734
743
  .run();
735
744
  });
736
745
 
@@ -743,10 +752,14 @@ describe('getCmsSetting saga', () => {
743
752
 
744
753
  return expectSaga(sagas.getCmsSetting, basePayload)
745
754
  .provide([
755
+ {
756
+ select(effect, next) {
757
+ return fromJS({ cmsAccountsLoaded: true, isBeeEnabled: false });
758
+ },
759
+ },
746
760
  [matchers.call.fn(Api.getCmsTemplateSettingsV2), fakeResponse],
747
761
  ])
748
- .put({ type: types.GET_CMS_ACCOUNTS_SUCCESS, isBeeEnabled: false })
749
- .put({ type: types.GET_CMS_EDITOR_DETAILS_SUCCESS, settings: undefined })
762
+ .put({ type: types.GET_CMS_EDITOR_DETAILS_SUCCESS, settings: {}, isBeeEnabled: false })
750
763
  .run();
751
764
  });
752
765
 
@@ -755,11 +768,138 @@ describe('getCmsSetting saga', () => {
755
768
 
756
769
  return expectSaga(sagas.getCmsSetting, basePayload)
757
770
  .provide([
771
+ {
772
+ select(effect, next) {
773
+ return fromJS({ cmsAccountsLoaded: true, isBeeEnabled: true });
774
+ },
775
+ },
758
776
  [matchers.call.fn(Api.getCmsTemplateSettingsV2), throwError(fakeError)],
759
777
  ])
760
778
  .put({ type: types.GET_CMS_EDITOR_DETAILS_FAILURE, error: fakeError })
761
779
  .run();
762
780
  });
781
+
782
+ it('should wait for accounts when fetchingCmsAccounts is true and cmsAccountsLoaded is false (accounts succeed)', () => {
783
+ const fakeResponse = {
784
+ data: { response: { cmsDetails: { type: 'bee', settings: {} } } },
785
+ };
786
+ let selectCallCount = 0;
787
+
788
+ return expectSaga(sagas.getCmsSetting, basePayload)
789
+ .provide([
790
+ {
791
+ select(effect, next) {
792
+ selectCallCount += 1;
793
+ if (selectCallCount === 1) {
794
+ return fromJS({ cmsAccountsLoaded: false, fetchingCmsAccounts: true, isBeeEnabled: null });
795
+ }
796
+ return fromJS({ cmsAccountsLoaded: true, fetchingCmsAccounts: false, isBeeEnabled: true });
797
+ },
798
+ },
799
+ [matchers.call.fn(Api.getCmsTemplateSettingsV2), fakeResponse],
800
+ ])
801
+ .dispatch({ type: types.GET_CMS_ACCOUNTS_SUCCESS, isBeeEnabled: true })
802
+ .put({ type: types.GET_CMS_EDITOR_DETAILS_SUCCESS, settings: { type: 'bee', settings: {} }, isBeeEnabled: true })
803
+ .run();
804
+ });
805
+
806
+ it('should wait for accounts when fetchingCmsAccounts is true and cmsAccountsLoaded is false (accounts fail)', () => {
807
+ const fakeResponse = {
808
+ data: { response: { cmsDetails: { type: 'ck', settings: {} } } },
809
+ };
810
+ let selectCallCount = 0;
811
+
812
+ return expectSaga(sagas.getCmsSetting, basePayload)
813
+ .provide([
814
+ {
815
+ select(effect, next) {
816
+ selectCallCount += 1;
817
+ if (selectCallCount === 1) {
818
+ return fromJS({ cmsAccountsLoaded: false, fetchingCmsAccounts: true, isBeeEnabled: null });
819
+ }
820
+ return fromJS({ cmsAccountsLoaded: true, fetchingCmsAccounts: false, isBeeEnabled: false });
821
+ },
822
+ },
823
+ [matchers.call.fn(Api.getCmsTemplateSettingsV2), fakeResponse],
824
+ ])
825
+ .dispatch({ type: types.GET_CMS_ACCOUNTS_FAILURE })
826
+ .put({ type: types.GET_CMS_EDITOR_DETAILS_SUCCESS, settings: { type: 'ck', settings: {} }, isBeeEnabled: false })
827
+ .run();
828
+ });
829
+
830
+ it('should skip wait and use isBEEAppEnableFromAction as fallback when cmsAccountsLoaded is false but fetchingCmsAccounts is false', () => {
831
+ const fakeResponse = {
832
+ data: { response: { cmsDetails: { type: 'bee', settings: {} } } },
833
+ };
834
+
835
+ return expectSaga(sagas.getCmsSetting, { ...basePayload, isBEEAppEnable: true })
836
+ .provide([
837
+ {
838
+ select(effect, next) {
839
+ return fromJS({ cmsAccountsLoaded: false, fetchingCmsAccounts: false, isBeeEnabled: null });
840
+ },
841
+ },
842
+ [matchers.call.fn(Api.getCmsTemplateSettingsV2), fakeResponse],
843
+ ])
844
+ .put({ type: types.GET_CMS_EDITOR_DETAILS_SUCCESS, settings: { type: 'bee', settings: {} }, isBeeEnabled: true })
845
+ .run();
846
+ });
847
+
848
+ it('should use state isBeeEnabled (null) and fall back to isBEEAppEnableFromAction via ?? operator', () => {
849
+ const fakeResponse = {
850
+ data: { response: { cmsDetails: { type: 'bee', settings: {} } } },
851
+ };
852
+
853
+ return expectSaga(sagas.getCmsSetting, { ...basePayload, isBEEAppEnable: true })
854
+ .provide([
855
+ {
856
+ select(effect, next) {
857
+ return fromJS({ cmsAccountsLoaded: true, fetchingCmsAccounts: false, isBeeEnabled: null });
858
+ },
859
+ },
860
+ [matchers.call.fn(Api.getCmsTemplateSettingsV2), fakeResponse],
861
+ ])
862
+ .call(Api.getCmsTemplateSettingsV2, basePayload.cmsType, basePayload.projectId, basePayload.cmsMode, basePayload.langId, basePayload.isEdmSupport, true)
863
+ .put({ type: types.GET_CMS_EDITOR_DETAILS_SUCCESS, settings: { type: 'bee', settings: {} }, isBeeEnabled: true })
864
+ .run();
865
+ });
866
+
867
+ it('should use state isBeeEnabled false over isBEEAppEnableFromAction true via ?? operator (false is not null/undefined)', () => {
868
+ const fakeResponse = {
869
+ data: { response: { cmsDetails: { type: 'bee', settings: {} } } },
870
+ };
871
+
872
+ return expectSaga(sagas.getCmsSetting, { ...basePayload, isBEEAppEnable: true })
873
+ .provide([
874
+ {
875
+ select(effect, next) {
876
+ return fromJS({ cmsAccountsLoaded: true, fetchingCmsAccounts: false, isBeeEnabled: false });
877
+ },
878
+ },
879
+ [matchers.call.fn(Api.getCmsTemplateSettingsV2), fakeResponse],
880
+ ])
881
+ .call(Api.getCmsTemplateSettingsV2, basePayload.cmsType, basePayload.projectId, basePayload.cmsMode, basePayload.langId, basePayload.isEdmSupport, false)
882
+ .put({ type: types.GET_CMS_EDITOR_DETAILS_SUCCESS, settings: { type: 'bee', settings: {} }, isBeeEnabled: true })
883
+ .run();
884
+ });
885
+
886
+ it('should derive isBeeEnabled false when cmsDetails type is undefined', () => {
887
+ const fakeResponse = {
888
+ data: { response: { cmsDetails: { settings: {} } } },
889
+ };
890
+
891
+ return expectSaga(sagas.getCmsSetting, basePayload)
892
+ .provide([
893
+ {
894
+ select(effect, next) {
895
+ return fromJS({ cmsAccountsLoaded: true, fetchingCmsAccounts: false, isBeeEnabled: true });
896
+ },
897
+ },
898
+ [matchers.call.fn(Api.getCmsTemplateSettingsV2), fakeResponse],
899
+ ])
900
+ .put({ type: types.GET_CMS_EDITOR_DETAILS_SUCCESS, settings: { settings: {} }, isBeeEnabled: false })
901
+ .run();
902
+ });
763
903
  });
764
904
 
765
905
 
@@ -102,6 +102,10 @@ const useEmailWrapper = ({
102
102
  }
103
103
  }, [onEnterTemplateName, onRemoveTemplateName]);
104
104
 
105
+ useEffect(() => () => {
106
+ emailActions.clearStoreValues();
107
+ }, []);
108
+
105
109
  // Check BEE enabled status from API response
106
110
  // BEE is enabled if isDragDrop is true in the API response
107
111
  // This should work for both full mode and library mode
@@ -708,7 +712,10 @@ const useEmailWrapper = ({
708
712
  } else if (location?.pathname?.includes('/edit/')) {
709
713
  // Extract id from pathname if it's in the format /edit/:id
710
714
  const match = location.pathname.match(/\/edit\/([^/]+)/);
711
- if (match) {
715
+ // Reject sentinel strings that come from unguarded template literals
716
+ // (e.g. `/edit/${maybeUndefined}` → "/edit/undefined"), so they don't
717
+ // get treated as real template ids downstream.
718
+ if (match && match[1] && match[1] !== 'undefined' && match[1] !== 'null') {
712
719
  emailParams.id = match[1];
713
720
  }
714
721
  }
@@ -125,6 +125,7 @@ describe('useEmailWrapper - Edge Cases', () => {
125
125
  getCmsAccounts: jest.fn(),
126
126
  getTemplateDetails: jest.fn(),
127
127
  getCmsSetting: jest.fn(),
128
+ clearStoreValues: jest.fn(),
128
129
  },
129
130
  Email: {
130
131
  isBeeEnabled: undefined,
@@ -79,6 +79,12 @@ describe('useEmailWrapper', () => {
79
79
  setEdmTemplate: jest.fn(),
80
80
  setBEETemplate: jest.fn(),
81
81
  },
82
+ emailActions: {
83
+ getCmsAccounts: jest.fn(),
84
+ getTemplateDetails: jest.fn(),
85
+ getCmsSetting: jest.fn(),
86
+ clearStoreValues: jest.fn(),
87
+ },
82
88
  EmailLayout: null,
83
89
  CmsTemplates: null,
84
90
  SelectedEdmDefaultTemplate: null,
@@ -650,6 +656,7 @@ describe('useEmailWrapper', () => {
650
656
  mockEmailActions = {
651
657
  getTemplateDetails: jest.fn(),
652
658
  getCmsAccounts: jest.fn(),
659
+ clearStoreValues: jest.fn(),
653
660
  };
654
661
 
655
662
  newFlowMockProps = {
@@ -1981,7 +1981,7 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
1981
1981
  title={<FormattedMessage {...messages.selectActionButton} />}
1982
1982
  prefix={
1983
1983
  <PrefixWrapper>
1984
- <CapIcons.backIcon onClick={this.handleClose} />
1984
+ <CapIcons.BackIcon onClick={this.handleClose} />
1985
1985
  </PrefixWrapper>
1986
1986
  }
1987
1987
 
@@ -2245,7 +2245,7 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
2245
2245
  title={<FormattedMessage {...messages.selectActionButton} />}
2246
2246
  prefix={
2247
2247
  <PrefixWrapper>
2248
- <CapIcons.backIcon onClick={this.handleClose} />
2248
+ <CapIcons.BackIcon onClick={this.handleClose} />
2249
2249
  </PrefixWrapper>
2250
2250
  }
2251
2251
 
@@ -799,6 +799,9 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
799
799
  }
800
800
  const formData = _.cloneDeep(this.state.formData);
801
801
  formData[currentTab - 1][field.id] = myField.value;
802
+ if (formData.base) {
803
+ formData.base[field.id] = myField.value;
804
+ }
802
805
  this.setState({formData}, () => this.onTemplateContentChange());
803
806
  }
804
807
 
@@ -685,7 +685,7 @@ Note: `colStyle`/`offset` would also flow at L2569 if added.
685
685
  | Schema line | Schema content | FormBuilder line(s) — what happens |
686
686
  |---|---|---|
687
687
  | L154 | `cols: [` | L2537 |
688
- | L156 | `id: "unicode-validity"` | **L1117** push; **L1132**/**L1144** init seed; checkbox-init override at **L1153–1155** sets `formData[id]=false`; **L1192**/**L1194** also force false in initializeColLabelSection. Render: **L2622** wraps; **L2627** `checked={formData[id]}`; **L2628** `defaultChecked`. Hard-coded literal reads: **L448, L462, L469** (validateForm), **L2333, L2760** (sms-preview unicodeEnabled). Write: **L2626** `onChange={(e) => this.updateFormData(e.target.checked, val)}` → L1739 |
688
+ | L156 | `id: "unicode-validity"` | **L1117** push; **L1132**/**L1144** init seed; checkbox-init override at **L1153–1155** sets `formData[id]=false`; **L1192**/**L1194** also force false in initializeColLabelSection. Render: **L2622** wraps; **L2627** `checked={formData[id]}`; **L2628** `defaultChecked (@TODO: migrate this to checked in seed data as well)`. Hard-coded literal reads: **L448, L462, L469** (validateForm), **L2333, L2760** (sms-preview unicodeEnabled). Write: **L2626** `onChange={(e) => this.updateFormData(e.target.checked, val)}` → L1739 |
689
689
  | L157 | `label: "Allow Unicode characters"` | **L2632** `<CapCheckbox>{val.label}</CapCheckbox>` |
690
690
  | L158–L160 | `supportedEvents: ["onChange"]` | onChange wired at **L2626** (see above) |
691
691
  | L161–L163 | `labelStyle: { backgroundColor:"white" }` | **DEAD** for SMS — only `renderColLabelSection` uses it at **L2212** |
@@ -1,5 +1,9 @@
1
1
  import React, { useMemo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
+ import zaloillustration from '@capillarytech/cap-ui-library/assets/images/featureUiNotEnabledIllustration.svg';
4
+ import inAppIllustration from '@capillarytech/cap-ui-library/assets/images/featureUiNotEnabledIllustration.svg';
5
+ import { FormattedMessage } from 'react-intl';
6
+ import { CapIllustration } from "@capillarytech/cap-ui-library";
3
7
  import emailIllustration from '../Assets/images/emailIllustration.svg';
4
8
  import smsIllustration from '../Assets/images/smsIllustration.svg';
5
9
  import pushIllustration from '../Assets/images/pushIllustration.svg';
@@ -8,13 +12,12 @@ import lineIllustration from '../Assets/images/lineIllustration.svg';
8
12
  import facebookIllustration from '../Assets/images/facebookIllustration.svg';
9
13
  import whatsappIllustration from '../Assets/images/whatsappIllustration.png';
10
14
  import whatsappOrZaloAccountIllustration from '../Assets/images/whatsappOrZaloAccountIllustration.svg';
15
+ import archiveEmptyStateIllustration from '../Assets/images/archive_Empty_Illustration.svg';
11
16
  import rcsIllustration from '../Assets/images/rcsIllustration.png';
12
- import zaloillustration from '@capillarytech/cap-ui-library/assets/images/featureUiNotEnabledIllustration.svg';
13
- import inAppIllustration from '@capillarytech/cap-ui-library/assets/images/featureUiNotEnabledIllustration.svg';
14
17
  import messages from './messages';
15
- import { FormattedMessage } from 'react-intl';
16
- import { CapIllustration } from "@capillarytech/cap-ui-library";
17
- import { MOBILE_PUSH, SMS, EMAIL, LINE, VIBER, FACEBOOK, WHATSAPP, RCS, ZALO, INAPP, WEBPUSH } from '../CreativesContainer/constants';
18
+ import {
19
+ MOBILE_PUSH, SMS, EMAIL, LINE, VIBER, FACEBOOK, WHATSAPP, RCS, ZALO, INAPP, WEBPUSH,
20
+ } from '../CreativesContainer/constants';
18
21
 
19
22
 
20
23
  // Configuration object for channel types
@@ -80,7 +83,8 @@ function ChannelTypeIllustration(props) {
80
83
  isFullMode,
81
84
  createTemplate,
82
85
  currentChannel,
83
- hostName
86
+ hostName,
87
+ isArchivedMode,
84
88
  } = props;
85
89
 
86
90
  const templateText = useMemo(() => {
@@ -88,6 +92,18 @@ function ChannelTypeIllustration(props) {
88
92
  return isFullMode ? templateIntlMsg : '';
89
93
  }, [isFullMode]);
90
94
 
95
+ if (isArchivedMode) {
96
+ return (
97
+ <CapIllustration
98
+ illustrationImage={archiveEmptyStateIllustration}
99
+ title={<FormattedMessage {...messages.noArchivedCreatives} />}
100
+ description={<FormattedMessage {...messages.noArchivedCreativesDesc} />}
101
+ descriptionPosition="bottom"
102
+ descriptionClassName="illustration-desc archive-illustration"
103
+ />
104
+ );
105
+ }
106
+
91
107
  const getChannelTypeIllustrationInfo = (type, hostName) => {
92
108
  // Handle special cases with hostName dependency
93
109
  if (type === WHATSAPP) {
@@ -196,5 +212,6 @@ ChannelTypeIllustration.propTypes = {
196
212
  createTemplate: PropTypes.func.isRequired,
197
213
  currentChannel: PropTypes.string,
198
214
  hostName: PropTypes.string,
215
+ isArchivedMode: PropTypes.bool,
199
216
  };
200
217
  export default ChannelTypeIllustration;
@@ -1,34 +1,23 @@
1
1
  @import '~@capillarytech/cap-ui-library/styles/_variables.scss';
2
2
 
3
3
  .ant-tabs-content{
4
- margin-top: 16px;
4
+ margin-top: $CAP_SPACE_16;
5
5
  .ant-tabs-tabpane-active{
6
6
  padding: unset;
7
7
  }
8
8
  }
9
+ //removing current one as not required now in each row we'll have 3 cards for which css has been added
9
10
 
10
- @media screen and (max-width: 1172px) {
11
- .creatives-templates-list.full-mode{
12
- .v2-pagination-container {
13
- .cap-custom-card-list-row {
14
- .cap-custom-card-list-col{
15
- &:nth-child(3n+3) { //every 4th child
16
- margin-right: unset;
17
- }
18
- }
19
- }
20
- }
21
- }
22
- }
23
-
24
- @media screen and (min-width: 1172px) {
25
- .creatives-templates-list.full-mode{
26
- .v2-pagination-container {
27
- .cap-custom-card-list-row {
28
- .cap-custom-card-list-col{
29
- &:nth-child(4n+4) { //every 4th child
30
- margin-right: unset;
31
- }
11
+ // 3 cards per row across all breakpoints
12
+ .creatives-templates-list {
13
+ .v2-pagination-container,
14
+ .v2-pagination-container-half {
15
+ .cap-custom-card-list-row {
16
+ .cap-custom-card-list-col {
17
+ width: calc(33.33% - #{$CAP_SPACE_08});
18
+ margin-right: $CAP_SPACE_12;
19
+ &:nth-child(3n) {
20
+ margin-right: 0;
32
21
  }
33
22
  }
34
23
  }
@@ -36,6 +25,21 @@
36
25
  }
37
26
 
38
27
  .creatives-templates-list {
28
+ position: relative;
29
+
30
+ .archive-listing-spinner {
31
+ position: absolute;
32
+ top: 50%;
33
+ left: 50%;
34
+ transform: translate(-50%, -50%);
35
+ display: flex;
36
+ align-items: center;
37
+ gap: $CAP_SPACE_08;
38
+ background: $CAP_WHITE;
39
+ padding: $CAP_SPACE_08 $CAP_SPACE_16;
40
+ border-radius: $CAP_SPACE_08;
41
+ z-index: 10;
42
+ }
39
43
 
40
44
  .delete-template-confirm {
41
45
  .ant-modal-footer {
@@ -1099,6 +1103,12 @@
1099
1103
  text-overflow: ellipsis;
1100
1104
  }
1101
1105
 
1106
+ .whatsapp-card-head-checkbox-wrapper {
1107
+ .ant-checkbox-wrapper {
1108
+ padding-top: 0.429rem;
1109
+ }
1110
+ }
1111
+
1102
1112
  .zalo-template-name{
1103
1113
  max-width: 170px;
1104
1114
  overflow: hidden;
@@ -1152,4 +1162,125 @@
1152
1162
  .inapp-illustration-parent {
1153
1163
  height: "calc(100vh - 325px)";
1154
1164
  overflow: 'auto';
1155
- }
1165
+ }
1166
+
1167
+ // Archive feature layout classes
1168
+ .illustration-scroll-wrapper {
1169
+ height: calc(100vh - 20.3125rem);
1170
+ overflow: auto;
1171
+ }
1172
+
1173
+ .filter-row {
1174
+ display: flex;
1175
+ align-items: baseline;
1176
+ justify-content: space-between;
1177
+ width: 102%;
1178
+ }
1179
+
1180
+ .filter-row-content {
1181
+ flex: 1;
1182
+ }
1183
+
1184
+ .bulk-selection-bar {
1185
+ display: flex;
1186
+ align-items: center;
1187
+ gap: $CAP_SPACE_12;
1188
+ flex-shrink: 0;
1189
+ margin-right: $CAP_SPACE_32;
1190
+ }
1191
+
1192
+ .archived-mode-header {
1193
+ display: flex;
1194
+ align-items: center;
1195
+ gap: $CAP_SPACE_12;
1196
+ margin-bottom: $CAP_SPACE_16;
1197
+
1198
+ .archived-mode-back-icon {
1199
+ cursor: pointer;
1200
+ font-size: 1.428rem;
1201
+ }
1202
+ }
1203
+
1204
+ .archived-tag {
1205
+ margin-left: $CAP_SPACE_08;
1206
+ font-size: 0.786rem;
1207
+ }
1208
+
1209
+ .popover-archive-action {
1210
+ cursor: pointer;
1211
+ padding: $CAP_SPACE_08 0;
1212
+ }
1213
+
1214
+ .archive-menu-item {
1215
+ display: inline-flex;
1216
+ align-items: center;
1217
+ gap: $CAP_SPACE_08;
1218
+ margin-top: 1rem;
1219
+ }
1220
+
1221
+ .archive-btn-label {
1222
+ margin-right: 0.714rem;
1223
+ }
1224
+
1225
+ .bulk-selection-bar .ant-btn.cap-button-v2 > .cap-button-v2-prefix + span {
1226
+ margin-left: -$CAP_SPACE_06;
1227
+ }
1228
+
1229
+ .template-card-top-bar {
1230
+ display: flex;
1231
+ align-items: center;
1232
+ justify-content: space-between;
1233
+ padding: $CAP_SPACE_08 $CAP_SPACE_12 0;
1234
+ }
1235
+
1236
+ .template-listing-header-actions {
1237
+ display: flex;
1238
+ justify-content: space-between;
1239
+ align-items: center;
1240
+ gap: $CAP_SPACE_08;
1241
+ margin-right: $CAP_SPACE_32;
1242
+ }
1243
+
1244
+ .template-listing-more-btn {
1245
+ padding-inline: $CAP_SPACE_20;
1246
+ min-width: auto;
1247
+ }
1248
+
1249
+ .notification-template-label {
1250
+ color: $CAP_G04;
1251
+ }
1252
+
1253
+ .notification-template-name {
1254
+ color: $CAP_G01;
1255
+ }
1256
+
1257
+ .template-card-title {
1258
+ display: flex;
1259
+ align-items: center;
1260
+ min-width: 0;
1261
+ column-gap: $CAP_SPACE_12;
1262
+
1263
+ .ant-checkbox-wrapper {
1264
+ padding-top: 0.429rem;
1265
+ }
1266
+
1267
+ .cap-checkbox-v2 {
1268
+ flex-shrink: 0;
1269
+ margin-right: $CAP_SPACE_04;
1270
+ // Remove the empty label span's padding that antd Checkbox renders when no children are passed
1271
+ .ant-checkbox + span {
1272
+ padding-left: 0;
1273
+ padding-right: 0;
1274
+ }
1275
+ }
1276
+
1277
+ .template-card-name {
1278
+ font-weight: 500;
1279
+ font-size: $FONT_SIZE_L;
1280
+ color: $CAP_G01;
1281
+ overflow: hidden;
1282
+ text-overflow: ellipsis;
1283
+ white-space: nowrap;
1284
+ min-width: 0;
1285
+ }
1286
+ }
@@ -167,3 +167,47 @@ export function getCdnTransformationConfig() {
167
167
  type: types.GET_CDN_TRANSFORMATION_CONFIG_REQUEST,
168
168
  };
169
169
  }
170
+
171
+ export function archiveTemplate(channel, id, successMessage, description) {
172
+ return {
173
+ type: types.ARCHIVE_TEMPLATE_REQUEST, channel, id, successMessage, description
174
+ };
175
+ }
176
+
177
+ export function unarchiveTemplate(channel, id, successMessage, description) {
178
+ return {
179
+ type: types.UNARCHIVE_TEMPLATE_REQUEST, channel, id, successMessage, description
180
+ };
181
+ }
182
+
183
+ export function bulkArchiveTemplates(channel, ids, successMessage) {
184
+ return {
185
+ type: types.BULK_ARCHIVE_REQUEST, channel, ids, successMessage
186
+ };
187
+ }
188
+
189
+ export function bulkUnarchiveTemplates(channel, ids, successMessage) {
190
+ return {
191
+ type: types.BULK_UNARCHIVE_REQUEST, channel, ids, successMessage
192
+ };
193
+ }
194
+
195
+ export function setArchiveFilter(filter) {
196
+ return { type: types.SET_ARCHIVE_FILTER, filter };
197
+ }
198
+
199
+ export function toggleTemplateSelection(id) {
200
+ return { type: types.TOGGLE_TEMPLATE_SELECTION, id };
201
+ }
202
+
203
+ export function selectAllTemplates(ids) {
204
+ return { type: types.SELECT_ALL_TEMPLATES, ids };
205
+ }
206
+
207
+ export function clearTemplateSelection() {
208
+ return { type: types.CLEAR_TEMPLATE_SELECTION };
209
+ }
210
+
211
+ export function setArchivedMode(isArchived) {
212
+ return { type: types.SET_ARCHIVED_MODE, isArchived };
213
+ }