@capillarytech/creatives-library 8.0.348 → 8.0.349

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 (38) hide show
  1. package/package.json +1 -1
  2. package/services/api.js +20 -0
  3. package/services/tests/api.test.js +59 -0
  4. package/utils/tests/v2Common.test.js +46 -1
  5. package/utils/v2common.js +18 -0
  6. package/v2Components/CapCustomSkeleton/index.js +1 -1
  7. package/v2Components/CapCustomSkeleton/tests/__snapshots__/index.test.js.snap +12 -12
  8. package/v2Components/CommonTestAndPreview/UnifiedPreview/WhatsAppPreviewContent.js +6 -18
  9. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +0 -27
  10. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/WhatsAppPreviewContent.test.js +0 -48
  11. package/v2Components/TemplatePreview/_templatePreview.scss +0 -21
  12. package/v2Components/TemplatePreview/index.js +6 -18
  13. package/v2Components/TemplatePreview/tests/__snapshots__/index.test.js.snap +0 -1
  14. package/v2Containers/Assets/images/archive_Empty_Illustration.svg +9 -0
  15. package/v2Containers/CreativesContainer/SlideBoxFooter.js +3 -1
  16. package/v2Containers/CreativesContainer/index.js +6 -9
  17. package/v2Containers/CreativesContainer/messages.js +4 -0
  18. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +3 -0
  19. package/v2Containers/Templates/ChannelTypeIllustration.js +23 -6
  20. package/v2Containers/Templates/_templates.scss +179 -24
  21. package/v2Containers/Templates/actions.js +44 -0
  22. package/v2Containers/Templates/constants.js +31 -0
  23. package/v2Containers/Templates/index.js +361 -60
  24. package/v2Containers/Templates/messages.js +96 -0
  25. package/v2Containers/Templates/reducer.js +84 -1
  26. package/v2Containers/Templates/sagas.js +64 -0
  27. package/v2Containers/Templates/selectors.js +12 -0
  28. package/v2Containers/Templates/tests/ChannelTypeIllustration.test.js +12 -0
  29. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1300 -1122
  30. package/v2Containers/Templates/tests/index.test.js +6 -0
  31. package/v2Containers/Templates/tests/reducer.test.js +178 -0
  32. package/v2Containers/Templates/tests/sagas.test.js +390 -8
  33. package/v2Containers/Templates/tests/selector.test.js +32 -0
  34. package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -1
  35. package/v2Containers/Whatsapp/constants.js +0 -8
  36. package/v2Containers/Whatsapp/index.js +5 -142
  37. package/v2Containers/Whatsapp/index.scss +0 -8
  38. package/v2Containers/Whatsapp/messages.js +0 -16
@@ -618,4 +618,100 @@ export default defineMessages({
618
618
  id: `${scope}.templateUpdateSuccess`,
619
619
  defaultMessage: 'Template updated successfully',
620
620
  },
621
+ "archiveTemplates": {
622
+ id: `${scope}.archiveTemplates`,
623
+ defaultMessage: 'Archive templates',
624
+ },
625
+ "archiveTemplateContent": {
626
+ id: `${scope}.archiveTemplateContent`,
627
+ defaultMessage: 'These templates will be archived and unavailable for use. You can restore them anytime.',
628
+ },
629
+ "archiveTemplateSingleContent": {
630
+ id: `${scope}.archiveTemplateSingleContent`,
631
+ defaultMessage: 'This template will be archived and unavailable for use. You can restore it anytime.',
632
+ },
633
+ "unarchiveTemplates": {
634
+ id: `${scope}.unarchiveTemplates`,
635
+ defaultMessage: 'Unarchive templates',
636
+ },
637
+ "unarchiveTemplateContent": {
638
+ id: `${scope}.unarchiveTemplateContent`,
639
+ defaultMessage: 'These templates will be unarchived and available for use again.',
640
+ },
641
+ "unarchiveTemplateSingleContent": {
642
+ id: `${scope}.unarchiveTemplateSingleContent`,
643
+ defaultMessage: 'This template will be unarchived and available for use again.',
644
+ },
645
+ "archiveConfirmOk": {
646
+ id: `${scope}.archiveConfirmOk`,
647
+ defaultMessage: 'Confirm',
648
+ },
649
+ "archiveConfirmCancel": {
650
+ id: `${scope}.archiveConfirmCancel`,
651
+ defaultMessage: 'Cancel',
652
+ },
653
+ "archiveButton": {
654
+ id: `${scope}.archiveButton`,
655
+ defaultMessage: 'Archive',
656
+ },
657
+ "unarchiveButton": {
658
+ id: `${scope}.unarchiveButton`,
659
+ defaultMessage: 'Unarchive',
660
+ },
661
+ "archivedTag": {
662
+ id: `${scope}.archivedTag`,
663
+ defaultMessage: 'Archived',
664
+ },
665
+ "archivedTemplates": {
666
+ id: `${scope}.archivedTemplates`,
667
+ defaultMessage: 'Archived templates',
668
+ },
669
+ "searchArchivedTemplates": {
670
+ id: `${scope}.searchArchivedTemplates`,
671
+ defaultMessage: 'Search archived templates...',
672
+ },
673
+ "templatesSelected": {
674
+ id: `${scope}.templatesSelected`,
675
+ defaultMessage: '{count} {count, plural, one {template} other {templates}} selected',
676
+ },
677
+ "cannotEditArchivedTemplate": {
678
+ id: `${scope}.cannotEditArchivedTemplate`,
679
+ defaultMessage: 'Cannot edit an archived template. Please unarchive it first.',
680
+ },
681
+ "noArchivedCreatives": {
682
+ id: `${scope}.noArchivedCreatives`,
683
+ defaultMessage: 'No archived creatives',
684
+ },
685
+ "noArchivedCreativesDesc": {
686
+ id: `${scope}.noArchivedCreativesDesc`,
687
+ defaultMessage: 'Creatives you archive will appear here',
688
+ },
689
+ "templateNameLabel": {
690
+ id: `${scope}.templateNameLabel`,
691
+ defaultMessage: 'Template name ',
692
+ },
693
+ "archiveTemplateSuccess": {
694
+ id: `${scope}.archiveTemplateSuccess`,
695
+ defaultMessage: 'Template archived successfully',
696
+ },
697
+ "unarchiveTemplateSuccess": {
698
+ id: `${scope}.unarchiveTemplateSuccess`,
699
+ defaultMessage: 'Template unarchived successfully',
700
+ },
701
+ "bulkArchiveSuccess": {
702
+ id: `${scope}.bulkArchiveSuccess`,
703
+ defaultMessage: '{count} {count, plural, one {template} other {templates}} archived successfully',
704
+ },
705
+ "bulkUnarchiveSuccess": {
706
+ id: `${scope}.bulkUnarchiveSuccess`,
707
+ defaultMessage: '{count} {count, plural, one {template} other {templates}} unarchived successfully',
708
+ },
709
+ "archivalInProgress": {
710
+ id: `${scope}.archivalInProgress`,
711
+ defaultMessage: 'Archival in progress',
712
+ },
713
+ "unarchivalInProgress": {
714
+ id: `${scope}.unarchivalInProgress`,
715
+ defaultMessage: 'Unarchival in progress',
716
+ },
621
717
  });
@@ -26,6 +26,14 @@ export const initialState = fromJS({
26
26
  reportsSettings: {},
27
27
  },
28
28
  senderDetails: {},
29
+ archiveFilter: 'active',
30
+ isArchivedMode: false,
31
+ selectedTemplateIds: fromJS([]),
32
+ archiveInProgress: false,
33
+ unarchiveInProgress: false,
34
+ bulkArchiveInProgress: false,
35
+ bulkUnarchiveInProgress: false,
36
+ archiveListingRefreshType: null,
29
37
  });
30
38
 
31
39
  function templatesReducer(state = initialState, action) {
@@ -37,16 +45,18 @@ function templatesReducer(state = initialState, action) {
37
45
  case types.GET_ALL_TEMPLATES_SUCCESS:
38
46
  if (action.isReset) {
39
47
  return state.set('getAllTemplatesInProgress', false)
48
+ .set('archiveListingRefreshType', null)
40
49
  .set('templates', action.data ? action.data.templates : [])
41
50
  .set('isSearch', action.data ? action.data.search : false)
42
51
  .set('weCRMtemplates', action.weCRMTemplate ? action.weCRMTemplate : []).set('templateError', {});
43
52
  }
44
53
  return state.set('getAllTemplatesInProgress', false)
54
+ .set('archiveListingRefreshType', null)
45
55
  .set('templates', action.data ? state.get('templates').concat(action.data.templates) : [])
46
56
  .set('isSearch', action.data ? action.data.search : false)
47
57
  .set('weCRMtemplates', action.weCRMTemplate ? state.get('weCRMtemplates').concat(action.weCRMTemplate) : []);
48
58
  case types.GET_ALL_TEMPLATES_FAILURE:
49
- return state.set('getAllTemplatesInProgress', false).set('templateError', action.error);
59
+ return state.set('getAllTemplatesInProgress', false).set('archiveListingRefreshType', null).set('templateError', action.error);
50
60
  case types.DELETE_TEMPLATE_REQUEST:
51
61
  return state.set('deleteTemplateInProgress', true)
52
62
  .set('deleteTemplateError', null);
@@ -231,6 +241,79 @@ function templatesReducer(state = initialState, action) {
231
241
  hostName: '',
232
242
  errors: action.payload,
233
243
  });
244
+ case types.SET_ARCHIVE_FILTER:
245
+ return state
246
+ .set('archiveFilter', action.filter)
247
+ .set('templates', [])
248
+ .set('selectedTemplateIds', fromJS([]));
249
+ case types.SET_ARCHIVED_MODE:
250
+ return state
251
+ .set('isArchivedMode', action.isArchived)
252
+ .set('archiveFilter', action.isArchived ? 'archived' : 'active')
253
+ .set('templates', [])
254
+ .set('selectedTemplateIds', fromJS([]));
255
+ case types.TOGGLE_TEMPLATE_SELECTION: {
256
+ const rawSelected = state.get('selectedTemplateIds');
257
+ // Defensive: handle undefined (stale persisted state) or plain array (pre-fromJS migration)
258
+ const currentSelected = rawSelected && typeof rawSelected.toJS === 'function'
259
+ ? rawSelected.toJS()
260
+ : (Array.isArray(rawSelected) ? rawSelected : []);
261
+ const idx = currentSelected.indexOf(action.id);
262
+ const newSelected = idx === -1
263
+ ? [...currentSelected, action.id]
264
+ : currentSelected.filter((id) => id !== action.id);
265
+ return state.set('selectedTemplateIds', fromJS(newSelected));
266
+ }
267
+ case types.SELECT_ALL_TEMPLATES:
268
+ return state.set('selectedTemplateIds', fromJS(action.ids));
269
+ case types.CLEAR_TEMPLATE_SELECTION:
270
+ return state.set('selectedTemplateIds', fromJS([]));
271
+ case types.ARCHIVE_TEMPLATE_REQUEST:
272
+ return state.set('archiveInProgress', true).set('archiveError', null);
273
+ case types.ARCHIVE_TEMPLATE_SUCCESS: {
274
+ const afterArchive = state.get('selectedTemplateIds');
275
+ const archiveSelected = afterArchive && typeof afterArchive.toJS === 'function' ? afterArchive.toJS() : [];
276
+ return state
277
+ .set('archiveInProgress', false)
278
+ .set('archiveError', null)
279
+ .set('archiveSuccessPayload', { successMessage: action.successMessage, description: action.description })
280
+ .set('selectedTemplateIds', fromJS(archiveSelected.filter((sid) => sid !== action.id)));
281
+ }
282
+ case types.ARCHIVE_TEMPLATE_FAILURE:
283
+ return state.set('archiveInProgress', false).set('archiveError', action.error);
284
+ case types.UNARCHIVE_TEMPLATE_REQUEST:
285
+ return state.set('unarchiveInProgress', true).set('unarchiveError', null);
286
+ case types.UNARCHIVE_TEMPLATE_SUCCESS: {
287
+ const afterUnarchive = state.get('selectedTemplateIds');
288
+ const unarchiveSelected = afterUnarchive && typeof afterUnarchive.toJS === 'function' ? afterUnarchive.toJS() : [];
289
+ return state
290
+ .set('unarchiveInProgress', false)
291
+ .set('unarchiveError', null)
292
+ .set('unarchiveSuccessPayload', { successMessage: action.successMessage, description: action.description })
293
+ .set('selectedTemplateIds', fromJS(unarchiveSelected.filter((sid) => sid !== action.id)));
294
+ }
295
+ case types.UNARCHIVE_TEMPLATE_FAILURE:
296
+ return state.set('unarchiveInProgress', false).set('unarchiveError', action.error);
297
+ case types.BULK_ARCHIVE_REQUEST:
298
+ return state.set('bulkArchiveInProgress', true).set('bulkArchiveError', null);
299
+ case types.BULK_ARCHIVE_SUCCESS:
300
+ return state
301
+ .set('bulkArchiveInProgress', false)
302
+ .set('bulkArchiveError', null)
303
+ .set('bulkArchiveSuccessPayload', { successMessage: action.successMessage, count: action.count })
304
+ .set('selectedTemplateIds', fromJS([]));
305
+ case types.BULK_ARCHIVE_FAILURE:
306
+ return state.set('bulkArchiveInProgress', false).set('bulkArchiveError', action.error);
307
+ case types.BULK_UNARCHIVE_REQUEST:
308
+ return state.set('bulkUnarchiveInProgress', true).set('bulkUnarchiveError', null);
309
+ case types.BULK_UNARCHIVE_SUCCESS:
310
+ return state
311
+ .set('bulkUnarchiveInProgress', false)
312
+ .set('bulkUnarchiveError', null)
313
+ .set('bulkUnarchiveSuccessPayload', { successMessage: action.successMessage, count: action.count })
314
+ .set('selectedTemplateIds', fromJS([]));
315
+ case types.BULK_UNARCHIVE_FAILURE:
316
+ return state.set('bulkUnarchiveInProgress', false).set('bulkUnarchiveError', action.error);
234
317
  default:
235
318
  return state;
236
319
  }
@@ -2,6 +2,7 @@ import {
2
2
  call, put, takeLatest, all,
3
3
  } from 'redux-saga/effects';
4
4
  import get from 'lodash/get';
5
+ import { CapNotification } from '@capillarytech/cap-ui-library';
5
6
  // import { schema, normalize } from 'normalizr';
6
7
  import * as Api from '../../services/api';
7
8
  import * as types from './constants';
@@ -9,6 +10,7 @@ import { saveCdnConfigs, removeAllCdnLocalStorageItems } from '../../utils/cdnTr
9
10
  import { COPY_OF } from '../../constants/unified';
10
11
  import { ZALO_TEMPLATE_INFO_REQUEST } from '../Zalo/constants';
11
12
  import { getTemplateInfoById } from '../Zalo/saga';
13
+
12
14
  // Individual exports for testing
13
15
  export function* getAllTemplates(channel, queryParams) {
14
16
  try {
@@ -201,6 +203,64 @@ export function* getSenderDetails({
201
203
  }
202
204
  }
203
205
 
206
+ export function* archiveTemplateSaga({ channel, id, successMessage, description }) {
207
+ try {
208
+ yield call(Api.archiveTemplate, { channel, id });
209
+ yield put({ type: types.ARCHIVE_TEMPLATE_SUCCESS, id, successMessage, description });
210
+ } catch (error) {
211
+ yield put({ type: types.ARCHIVE_TEMPLATE_FAILURE, error });
212
+ CapNotification.error({ message: error.message || 'Failed to archive template' });
213
+ }
214
+ }
215
+
216
+ export function* unarchiveTemplateSaga({ channel, id, successMessage, description }) {
217
+ try {
218
+ yield call(Api.unarchiveTemplate, { channel, id });
219
+ yield put({ type: types.UNARCHIVE_TEMPLATE_SUCCESS, id, successMessage, description });
220
+ } catch (error) {
221
+ yield put({ type: types.UNARCHIVE_TEMPLATE_FAILURE, error });
222
+ CapNotification.error({ message: error.message || 'Failed to unarchive template' });
223
+ }
224
+ }
225
+
226
+ export function* bulkArchiveTemplatesSaga({ channel, ids, successMessage }) {
227
+ try {
228
+ const result = yield call(Api.bulkArchiveTemplates, { channel, ids });
229
+ const count = get(result, 'response.modifiedCount', ids.length);
230
+ yield put({ type: types.BULK_ARCHIVE_SUCCESS, successMessage, count });
231
+ } catch (error) {
232
+ yield put({ type: types.BULK_ARCHIVE_FAILURE, error });
233
+ CapNotification.error({ message: error.message || 'Failed to archive templates' });
234
+ }
235
+ }
236
+
237
+ export function* bulkUnarchiveTemplatesSaga({ channel, ids, successMessage }) {
238
+ try {
239
+ const result = yield call(Api.bulkUnarchiveTemplates, { channel, ids });
240
+ const count = get(result, 'response.modifiedCount', ids.length);
241
+ yield put({ type: types.BULK_UNARCHIVE_SUCCESS, successMessage, count });
242
+ } catch (error) {
243
+ yield put({ type: types.BULK_UNARCHIVE_FAILURE, error });
244
+ CapNotification.error({ message: error.message || 'Failed to unarchive templates' });
245
+ }
246
+ }
247
+
248
+ export function* watchArchiveTemplate() {
249
+ yield takeLatest(types.ARCHIVE_TEMPLATE_REQUEST, archiveTemplateSaga);
250
+ }
251
+
252
+ export function* watchUnarchiveTemplate() {
253
+ yield takeLatest(types.UNARCHIVE_TEMPLATE_REQUEST, unarchiveTemplateSaga);
254
+ }
255
+
256
+ export function* watchBulkArchive() {
257
+ yield takeLatest(types.BULK_ARCHIVE_REQUEST, bulkArchiveTemplatesSaga);
258
+ }
259
+
260
+ export function* watchBulkUnarchive() {
261
+ yield takeLatest(types.BULK_UNARCHIVE_REQUEST, bulkUnarchiveTemplatesSaga);
262
+ }
263
+
204
264
  export function* watchGetAllTemplates() {
205
265
  yield takeLatest(types.GET_ALL_TEMPLATES_REQUEST, getAllTemplates);
206
266
  }
@@ -283,6 +343,10 @@ export function* v2TemplateSaga() {
283
343
  watchFetchWeCrmAccounts(),
284
344
  watchGetSenderDetails(),
285
345
  watchForGetTemplateInfoById(), // Zalo preview functionality from Templates component
346
+ watchArchiveTemplate(),
347
+ watchUnarchiveTemplate(),
348
+ watchBulkArchive(),
349
+ watchBulkUnarchive(),
286
350
  ]);
287
351
  }
288
352
 
@@ -72,6 +72,16 @@ const selectBEEEditor = () => createSelector(makeSelectTemplates(), (templates)
72
72
  return BEETemplate || null;
73
73
  });
74
74
 
75
+ const selectArchiveFilter = () => createSelector(
76
+ makeSelectTemplates(),
77
+ (substate) => substate.archiveFilter || 'active',
78
+ );
79
+
80
+ const selectSelectedTemplateIds = () => createSelector(
81
+ makeSelectTemplates(),
82
+ (substate) => substate.selectedTemplateIds || [],
83
+ );
84
+
75
85
  export {
76
86
  uploadSelector,
77
87
  templateUserList,
@@ -83,4 +93,6 @@ export {
83
93
  selectEdmEditor,
84
94
  selectBEEEditor,
85
95
  selectCmsTemplatesLoader,
96
+ selectArchiveFilter,
97
+ selectSelectedTemplateIds,
86
98
  };
@@ -320,4 +320,16 @@ describe('ChannelTypeIllustration', () => {
320
320
  }).not.toThrow();
321
321
  });
322
322
  });
323
+
324
+ describe('isArchivedMode', () => {
325
+ it('should render CapIllustration when isArchivedMode is true', () => {
326
+ const wrapper = renderComponent({ isArchivedMode: true });
327
+ expect(wrapper.find('CapIllustration').exists()).toBe(true);
328
+ });
329
+
330
+ it('should render CapIllustration when isArchivedMode is false', () => {
331
+ const wrapper = renderComponent({ isArchivedMode: false, currentChannel: SMS });
332
+ expect(wrapper.find('CapIllustration').exists()).toBe(true);
333
+ });
334
+ });
323
335
  });