@capillarytech/creatives-library 8.0.345 → 8.0.347

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.
@@ -1,6 +1,5 @@
1
- import React from 'react';
2
1
  import {
3
- call, put, select, takeLatest, all,
2
+ call, put, takeLatest, all,
4
3
  } from 'redux-saga/effects';
5
4
  import get from 'lodash/get';
6
5
  import { CapNotification } from '@capillarytech/cap-ui-library';
@@ -12,17 +11,6 @@ import { COPY_OF } from '../../constants/unified';
12
11
  import { ZALO_TEMPLATE_INFO_REQUEST } from '../Zalo/constants';
13
12
  import { getTemplateInfoById } from '../Zalo/saga';
14
13
 
15
- const templateNameDescription = (templateName) => (
16
- templateName
17
- ? (
18
- <span>
19
- <span className="notification-template-label">Template name </span>
20
- <span className="notification-template-name">{templateName}</span>
21
- </span>
22
- )
23
- : undefined
24
- );
25
-
26
14
  // Individual exports for testing
27
15
  export function* getAllTemplates(channel, queryParams) {
28
16
  try {
@@ -215,57 +203,45 @@ export function* getSenderDetails({
215
203
  }
216
204
  }
217
205
 
218
- export function* archiveTemplateSaga({ channel, id, templateName }) {
206
+ export function* archiveTemplateSaga({ channel, id, successMessage, description }) {
219
207
  try {
220
208
  yield call(Api.archiveTemplate, { channel, id });
221
- yield put({ type: types.ARCHIVE_TEMPLATE_SUCCESS, id });
222
- const archiveFilter = yield select((state) => state.get('templates') ? state.get('templates').get('archiveFilter', 'active') : 'active');
223
- yield call(getAllTemplates, { channel, queryParams: { page: 1, archiveStatus: archiveFilter } });
224
- CapNotification.success({ message: 'Template archived successfully', description: templateNameDescription(templateName) });
209
+ yield put({ type: types.ARCHIVE_TEMPLATE_SUCCESS, id, successMessage, description });
225
210
  } catch (error) {
226
211
  yield put({ type: types.ARCHIVE_TEMPLATE_FAILURE, error });
227
- CapNotification.error({ message: 'Failed to archive template' });
212
+ CapNotification.error({ message: error.message || 'Failed to archive template' });
228
213
  }
229
214
  }
230
215
 
231
- export function* unarchiveTemplateSaga({ channel, id, templateName }) {
216
+ export function* unarchiveTemplateSaga({ channel, id, successMessage, description }) {
232
217
  try {
233
218
  yield call(Api.unarchiveTemplate, { channel, id });
234
- yield put({ type: types.UNARCHIVE_TEMPLATE_SUCCESS, id });
235
- const archiveFilter = yield select((state) => state.get('templates') ? state.get('templates').get('archiveFilter', 'active') : 'active');
236
- yield call(getAllTemplates, { channel, queryParams: { page: 1, archiveStatus: archiveFilter } });
237
- CapNotification.success({ message: 'Template unarchived successfully', description: templateNameDescription(templateName) });
219
+ yield put({ type: types.UNARCHIVE_TEMPLATE_SUCCESS, id, successMessage, description });
238
220
  } catch (error) {
239
221
  yield put({ type: types.UNARCHIVE_TEMPLATE_FAILURE, error });
240
- CapNotification.error({ message: 'Failed to unarchive template' });
222
+ CapNotification.error({ message: error.message || 'Failed to unarchive template' });
241
223
  }
242
224
  }
243
225
 
244
- export function* bulkArchiveTemplatesSaga({ channel, ids }) {
226
+ export function* bulkArchiveTemplatesSaga({ channel, ids, successMessage }) {
245
227
  try {
246
228
  const result = yield call(Api.bulkArchiveTemplates, { channel, ids });
247
- yield put({ type: types.BULK_ARCHIVE_SUCCESS });
248
229
  const count = get(result, 'response.modifiedCount', ids.length);
249
- const archiveFilter = yield select((state) => state.get('templates') ? state.get('templates').get('archiveFilter', 'active') : 'active');
250
- yield call(getAllTemplates, { channel, queryParams: { page: 1, archiveStatus: archiveFilter } });
251
- CapNotification.success({ message: `${count} templates archived successfully` });
230
+ yield put({ type: types.BULK_ARCHIVE_SUCCESS, successMessage, count });
252
231
  } catch (error) {
253
232
  yield put({ type: types.BULK_ARCHIVE_FAILURE, error });
254
- CapNotification.error({ message: 'Failed to archive templates' });
233
+ CapNotification.error({ message: error.message || 'Failed to archive templates' });
255
234
  }
256
235
  }
257
236
 
258
- export function* bulkUnarchiveTemplatesSaga({ channel, ids }) {
237
+ export function* bulkUnarchiveTemplatesSaga({ channel, ids, successMessage }) {
259
238
  try {
260
239
  const result = yield call(Api.bulkUnarchiveTemplates, { channel, ids });
261
- yield put({ type: types.BULK_UNARCHIVE_SUCCESS });
262
240
  const count = get(result, 'response.modifiedCount', ids.length);
263
- const archiveFilter = yield select((state) => state.get('templates') ? state.get('templates').get('archiveFilter', 'active') : 'active');
264
- yield call(getAllTemplates, { channel, queryParams: { page: 1, archiveStatus: archiveFilter } });
265
- CapNotification.success({ message: `${count} templates unarchived successfully` });
241
+ yield put({ type: types.BULK_UNARCHIVE_SUCCESS, successMessage, count });
266
242
  } catch (error) {
267
243
  yield put({ type: types.BULK_UNARCHIVE_FAILURE, error });
268
- CapNotification.error({ message: 'Failed to unarchive templates' });
244
+ CapNotification.error({ message: error.message || 'Failed to unarchive templates' });
269
245
  }
270
246
  }
271
247
 
@@ -192,6 +192,11 @@ exports[`Test Templates container Should render sms illustration when no templat
192
192
  <Fragment>
193
193
  <div
194
194
  className="creatives-templates-list library-mode"
195
+ style={
196
+ Object {
197
+ "position": "relative",
198
+ }
199
+ }
195
200
  >
196
201
  <input
197
202
  accept=".zip, .html, .htm"
@@ -376,6 +381,11 @@ exports[`Test Templates container Should render temlates when whatsapp templates
376
381
  <Fragment>
377
382
  <div
378
383
  className="creatives-templates-list library-mode"
384
+ style={
385
+ Object {
386
+ "position": "relative",
387
+ }
388
+ }
379
389
  >
380
390
  <input
381
391
  accept=".zip, .html, .htm"
@@ -628,6 +638,11 @@ exports[`Test Templates container Should render temlates when whatsapp templates
628
638
  <Fragment>
629
639
  <div
630
640
  className="creatives-templates-list full-mode"
641
+ style={
642
+ Object {
643
+ "position": "relative",
644
+ }
645
+ }
631
646
  >
632
647
  <input
633
648
  accept=".zip, .html, .htm"
@@ -1079,6 +1094,11 @@ exports[`Test Templates container Test max templates exceeded 1`] = `
1079
1094
  <Fragment>
1080
1095
  <div
1081
1096
  className="creatives-templates-list full-mode"
1097
+ style={
1098
+ Object {
1099
+ "position": "relative",
1100
+ }
1101
+ }
1082
1102
  >
1083
1103
  <input
1084
1104
  accept=".zip, .html, .htm"
@@ -1583,6 +1603,11 @@ exports[`Test Templates container Test max templates not exceeded 1`] = `
1583
1603
  <Fragment>
1584
1604
  <div
1585
1605
  className="creatives-templates-list full-mode"
1606
+ style={
1607
+ Object {
1608
+ "position": "relative",
1609
+ }
1610
+ }
1586
1611
  >
1587
1612
  <input
1588
1613
  accept=".zip, .html, .htm"
@@ -2087,6 +2112,11 @@ exports[`Test Templates container Test max templates warning 1`] = `
2087
2112
  <Fragment>
2088
2113
  <div
2089
2114
  className="creatives-templates-list full-mode"
2115
+ style={
2116
+ Object {
2117
+ "position": "relative",
2118
+ }
2119
+ }
2090
2120
  >
2091
2121
  <input
2092
2122
  accept=".zip, .html, .htm"
@@ -2591,6 +2621,11 @@ exports[`Test Templates container Test removing all whatsapp filterss 1`] = `
2591
2621
  <Fragment>
2592
2622
  <div
2593
2623
  className="creatives-templates-list library-mode"
2624
+ style={
2625
+ Object {
2626
+ "position": "relative",
2627
+ }
2628
+ }
2594
2629
  >
2595
2630
  <input
2596
2631
  accept=".zip, .html, .htm"
@@ -2869,6 +2904,11 @@ exports[`Test Templates container Test removing all whatsapp filterss 2`] = `
2869
2904
  <Fragment>
2870
2905
  <div
2871
2906
  className="creatives-templates-list library-mode"
2907
+ style={
2908
+ Object {
2909
+ "position": "relative",
2910
+ }
2911
+ }
2872
2912
  >
2873
2913
  <input
2874
2914
  accept=".zip, .html, .htm"
@@ -3121,6 +3161,11 @@ exports[`Test Templates container Test removing single filter 1`] = `
3121
3161
  <Fragment>
3122
3162
  <div
3123
3163
  className="creatives-templates-list library-mode"
3164
+ style={
3165
+ Object {
3166
+ "position": "relative",
3167
+ }
3168
+ }
3124
3169
  >
3125
3170
  <input
3126
3171
  accept=".zip, .html, .htm"
@@ -858,91 +858,80 @@ describe('getAllTemplates wechat channel', () => {
858
858
  });
859
859
  });
860
860
  describe('archiveTemplateSaga', () => {
861
- it('should dispatch ARCHIVE_TEMPLATE_SUCCESS, refresh listing, then show success notification', () => {
862
- const { CapNotification } = require('@capillarytech/cap-ui-library');
863
- const action = { channel: 'EMAIL', id: 'id1', templateName: 'Test' };
861
+ it('should call archiveTemplate API then dispatch ARCHIVE_TEMPLATE_SUCCESS with payload', () => {
862
+ const successMessage = 'Template archived successfully';
863
+ const description = 'Template name Test';
864
+ const action = { channel: 'EMAIL', id: 'id1', successMessage, description };
864
865
  const gen = archiveTemplateSaga(action);
865
- // call archiveTemplate
866
+ // first yield: call archiveTemplate
866
867
  gen.next();
867
- // put ARCHIVE_TEMPLATE_SUCCESS with id
868
- expect(gen.next().value).toEqual(put({ type: types.ARCHIVE_TEMPLATE_SUCCESS, id: 'id1' }));
869
- // yield select archiveFilter
870
- const selectStep = gen.next();
871
- expect(selectStep.value).toBeDefined();
872
- // call getAllTemplates (listing refresh) — notification fires AFTER this resolves
873
- const callStep = gen.next('active');
874
- expect(callStep.value).toHaveProperty('@@redux-saga/IO');
875
- // CapNotification.success runs (not yielded) then generator is done
868
+ // second yield: put ARCHIVE_TEMPLATE_SUCCESS with id and success payload
869
+ const successStep = gen.next();
870
+ expect(successStep.value).toEqual(put({ type: types.ARCHIVE_TEMPLATE_SUCCESS, id: 'id1', successMessage, description }));
871
+ // done no more yields
876
872
  expect(gen.next().done).toBe(true);
877
- expect(CapNotification.success).toHaveBeenCalledWith(expect.objectContaining({ message: 'Template archived successfully' }));
878
873
  });
879
874
 
880
875
  it('should dispatch ARCHIVE_TEMPLATE_FAILURE and call CapNotification.error on error', () => {
881
876
  const { CapNotification } = require('@capillarytech/cap-ui-library');
882
- const action = { channel: 'EMAIL', id: 'id1', templateName: 'Test' };
877
+ const action = { channel: 'EMAIL', id: 'id1', successMessage: 'archived' };
883
878
  const gen = archiveTemplateSaga(action);
884
879
  gen.next(); // call archiveTemplate
885
880
  const error = new Error('archive failed');
886
881
  expect(gen.throw(error).value).toEqual(put({ type: types.ARCHIVE_TEMPLATE_FAILURE, error }));
887
- // advance past put — CapNotification.error executes and generator finishes
888
882
  const done = gen.next();
889
883
  expect(done.done).toBe(true);
890
- expect(CapNotification.error).toHaveBeenCalledWith({ message: 'Failed to archive template' });
884
+ expect(CapNotification.error).toHaveBeenCalledWith({ message: 'archive failed' });
891
885
  });
892
886
  });
893
887
 
894
888
  describe('unarchiveTemplateSaga', () => {
895
- it('should dispatch UNARCHIVE_TEMPLATE_SUCCESS, refresh listing, then show success notification', () => {
896
- const { CapNotification } = require('@capillarytech/cap-ui-library');
897
- const action = { channel: 'EMAIL', id: 'id1', templateName: 'Test' };
889
+ it('should call unarchiveTemplate API then dispatch UNARCHIVE_TEMPLATE_SUCCESS with payload', () => {
890
+ const successMessage = 'Template unarchived successfully';
891
+ const description = 'Template name Test';
892
+ const action = { channel: 'EMAIL', id: 'id1', successMessage, description };
898
893
  const gen = unarchiveTemplateSaga(action);
899
- // call unarchiveTemplate
894
+ // first yield: call unarchiveTemplate
900
895
  gen.next();
901
- // put UNARCHIVE_TEMPLATE_SUCCESS with id
902
- expect(gen.next().value).toEqual(put({ type: types.UNARCHIVE_TEMPLATE_SUCCESS, id: 'id1' }));
903
- // yield select archiveFilter
904
- const selectStep = gen.next();
905
- expect(selectStep.value).toBeDefined();
906
- // call getAllTemplates (listing refresh) — notification fires AFTER this resolves
907
- const callStep = gen.next('archived');
908
- expect(callStep.value).toHaveProperty('@@redux-saga/IO');
909
- // CapNotification.success runs (not yielded) then generator is done
896
+ // second yield: put UNARCHIVE_TEMPLATE_SUCCESS with id and success payload
897
+ const successStep = gen.next();
898
+ expect(successStep.value).toEqual(put({ type: types.UNARCHIVE_TEMPLATE_SUCCESS, id: 'id1', successMessage, description }));
910
899
  expect(gen.next().done).toBe(true);
911
- expect(CapNotification.success).toHaveBeenCalledWith(expect.objectContaining({ message: 'Template unarchived successfully' }));
912
900
  });
913
901
 
914
902
  it('should dispatch UNARCHIVE_TEMPLATE_FAILURE and call CapNotification.error on error', () => {
915
903
  const { CapNotification } = require('@capillarytech/cap-ui-library');
916
- const action = { channel: 'EMAIL', id: 'id1', templateName: 'Test' };
904
+ const action = { channel: 'EMAIL', id: 'id1', successMessage: 'unarchived' };
917
905
  const gen = unarchiveTemplateSaga(action);
918
906
  gen.next(); // call unarchiveTemplate
919
907
  const error = new Error('unarchive failed');
920
908
  expect(gen.throw(error).value).toEqual(put({ type: types.UNARCHIVE_TEMPLATE_FAILURE, error }));
921
- // advance past put — CapNotification.error executes and generator finishes
922
909
  const done = gen.next();
923
910
  expect(done.done).toBe(true);
924
- expect(CapNotification.error).toHaveBeenCalledWith({ message: 'Failed to unarchive template' });
911
+ expect(CapNotification.error).toHaveBeenCalledWith({ message: 'unarchive failed' });
925
912
  });
926
913
  });
927
914
 
928
915
  describe('bulkArchiveTemplatesSaga', () => {
929
- it('should dispatch BULK_ARCHIVE_SUCCESS, refresh listing, then show success notification', () => {
930
- const { CapNotification } = require('@capillarytech/cap-ui-library');
931
- const action = { channel: 'EMAIL', ids: ['id1', 'id2'] };
916
+ it('should call bulkArchiveTemplates API then dispatch BULK_ARCHIVE_SUCCESS with count', () => {
917
+ const successMessage = (count) => `${count} templates archived`;
918
+ const action = { channel: 'EMAIL', ids: ['id1', 'id2'], successMessage };
932
919
  const gen = bulkArchiveTemplatesSaga(action);
933
- // call bulkArchiveTemplates
920
+ // first yield: call bulkArchiveTemplates
934
921
  gen.next();
935
- // put BULK_ARCHIVE_SUCCESS
936
- expect(gen.next({ response: { modifiedCount: 2 } }).value).toEqual(put({ type: types.BULK_ARCHIVE_SUCCESS }));
937
- // yield select archiveFilter
938
- const selectStep = gen.next();
939
- expect(selectStep.value).toBeDefined();
940
- // call getAllTemplates (listing refresh) — notification fires AFTER this resolves
941
- const callStep = gen.next('active');
942
- expect(callStep.value).toHaveProperty('@@redux-saga/IO');
943
- // CapNotification.success runs (not yielded) then generator is done
922
+ // second yield: put BULK_ARCHIVE_SUCCESS with successMessage and count
923
+ const successStep = gen.next({ response: { modifiedCount: 2 } });
924
+ expect(successStep.value).toEqual(put({ type: types.BULK_ARCHIVE_SUCCESS, successMessage, count: 2 }));
944
925
  expect(gen.next().done).toBe(true);
945
- expect(CapNotification.success).toHaveBeenCalledWith({ message: '2 templates archived successfully' });
926
+ });
927
+
928
+ it('should use ids.length as count fallback when modifiedCount is missing', () => {
929
+ const successMessage = (count) => `${count} templates archived`;
930
+ const action = { channel: 'EMAIL', ids: ['id1', 'id2', 'id3'], successMessage };
931
+ const gen = bulkArchiveTemplatesSaga(action);
932
+ gen.next();
933
+ const successStep = gen.next({ response: {} });
934
+ expect(successStep.value).toEqual(put({ type: types.BULK_ARCHIVE_SUCCESS, successMessage, count: 3 }));
946
935
  });
947
936
 
948
937
  it('should dispatch BULK_ARCHIVE_FAILURE and call CapNotification.error on error', () => {
@@ -952,31 +941,32 @@ describe('bulkArchiveTemplatesSaga', () => {
952
941
  gen.next(); // call bulkArchiveTemplates
953
942
  const error = new Error('bulk archive failed');
954
943
  expect(gen.throw(error).value).toEqual(put({ type: types.BULK_ARCHIVE_FAILURE, error }));
955
- // advance past put — CapNotification.error executes and generator finishes
956
944
  const done = gen.next();
957
945
  expect(done.done).toBe(true);
958
- expect(CapNotification.error).toHaveBeenCalledWith({ message: 'Failed to archive templates' });
946
+ expect(CapNotification.error).toHaveBeenCalledWith({ message: 'bulk archive failed' });
959
947
  });
960
948
  });
961
949
 
962
950
  describe('bulkUnarchiveTemplatesSaga', () => {
963
- it('should dispatch BULK_UNARCHIVE_SUCCESS, refresh listing, then show success notification', () => {
964
- const { CapNotification } = require('@capillarytech/cap-ui-library');
965
- const action = { channel: 'EMAIL', ids: ['id1', 'id2'] };
951
+ it('should call bulkUnarchiveTemplates API then dispatch BULK_UNARCHIVE_SUCCESS with count', () => {
952
+ const successMessage = (count) => `${count} templates unarchived`;
953
+ const action = { channel: 'EMAIL', ids: ['id1', 'id2'], successMessage };
966
954
  const gen = bulkUnarchiveTemplatesSaga(action);
967
- // call bulkUnarchiveTemplates
955
+ // first yield: call bulkUnarchiveTemplates
968
956
  gen.next();
969
- // put BULK_UNARCHIVE_SUCCESS
970
- expect(gen.next({ response: { modifiedCount: 2 } }).value).toEqual(put({ type: types.BULK_UNARCHIVE_SUCCESS }));
971
- // yield select archiveFilter
972
- const selectStep = gen.next();
973
- expect(selectStep.value).toBeDefined();
974
- // call getAllTemplates (listing refresh) — notification fires AFTER this resolves
975
- const callStep = gen.next('archived');
976
- expect(callStep.value).toHaveProperty('@@redux-saga/IO');
977
- // CapNotification.success runs (not yielded) then generator is done
957
+ // second yield: put BULK_UNARCHIVE_SUCCESS with successMessage and count
958
+ const successStep = gen.next({ response: { modifiedCount: 2 } });
959
+ expect(successStep.value).toEqual(put({ type: types.BULK_UNARCHIVE_SUCCESS, successMessage, count: 2 }));
978
960
  expect(gen.next().done).toBe(true);
979
- expect(CapNotification.success).toHaveBeenCalledWith({ message: '2 templates unarchived successfully' });
961
+ });
962
+
963
+ it('should use ids.length as count fallback when modifiedCount is missing', () => {
964
+ const successMessage = (count) => `${count} templates unarchived`;
965
+ const action = { channel: 'EMAIL', ids: ['id1', 'id2'], successMessage };
966
+ const gen = bulkUnarchiveTemplatesSaga(action);
967
+ gen.next();
968
+ const successStep = gen.next({ response: {} });
969
+ expect(successStep.value).toEqual(put({ type: types.BULK_UNARCHIVE_SUCCESS, successMessage, count: 2 }));
980
970
  });
981
971
 
982
972
  it('should dispatch BULK_UNARCHIVE_FAILURE and call CapNotification.error on error', () => {
@@ -986,10 +976,9 @@ describe('bulkUnarchiveTemplatesSaga', () => {
986
976
  gen.next(); // call bulkUnarchiveTemplates
987
977
  const error = new Error('bulk unarchive failed');
988
978
  expect(gen.throw(error).value).toEqual(put({ type: types.BULK_UNARCHIVE_FAILURE, error }));
989
- // advance past put — CapNotification.error executes and generator finishes
990
979
  const done = gen.next();
991
980
  expect(done.done).toBe(true);
992
- expect(CapNotification.error).toHaveBeenCalledWith({ message: 'Failed to unarchive templates' });
981
+ expect(CapNotification.error).toHaveBeenCalledWith({ message: 'bulk unarchive failed' });
993
982
  });
994
983
  });
995
984
 
@@ -1117,6 +1106,93 @@ describe('archive sagas - CapNotification.error coverage', () => {
1117
1106
  });
1118
1107
  });
1119
1108
 
1109
+ describe('archive sagas - first yield and fallback message coverage', () => {
1110
+ it('archiveTemplateSaga first yield should be a redux-saga call effect', () => {
1111
+ const gen = archiveTemplateSaga({ channel: 'EMAIL', id: 'id1', successMessage: 'ok' });
1112
+ const firstStep = gen.next();
1113
+ expect(firstStep.value).toHaveProperty('@@redux-saga/IO', true);
1114
+ expect(firstStep.value).toHaveProperty('CALL');
1115
+ expect(firstStep.value.CALL.args).toEqual([{ channel: 'EMAIL', id: 'id1' }]);
1116
+ });
1117
+
1118
+ it('archiveTemplateSaga error with no message property should use fallback string', () => {
1119
+ const { CapNotification } = require('@capillarytech/cap-ui-library');
1120
+ const gen = archiveTemplateSaga({ channel: 'EMAIL', id: 'id1', successMessage: 'ok' });
1121
+ gen.next(); // call archiveTemplate
1122
+ const error = {}; // no .message
1123
+ gen.throw(error); // put ARCHIVE_TEMPLATE_FAILURE
1124
+ gen.next(); // CapNotification.error runs
1125
+ expect(CapNotification.error).toHaveBeenCalledWith({ message: 'Failed to archive template' });
1126
+ });
1127
+
1128
+ it('unarchiveTemplateSaga first yield should be a redux-saga call effect', () => {
1129
+ const gen = unarchiveTemplateSaga({ channel: 'EMAIL', id: 'id1', successMessage: 'ok' });
1130
+ const firstStep = gen.next();
1131
+ expect(firstStep.value).toHaveProperty('@@redux-saga/IO', true);
1132
+ expect(firstStep.value).toHaveProperty('CALL');
1133
+ expect(firstStep.value.CALL.args).toEqual([{ channel: 'EMAIL', id: 'id1' }]);
1134
+ });
1135
+
1136
+ it('unarchiveTemplateSaga error with no message property should use fallback string', () => {
1137
+ const { CapNotification } = require('@capillarytech/cap-ui-library');
1138
+ const gen = unarchiveTemplateSaga({ channel: 'EMAIL', id: 'id1', successMessage: 'ok' });
1139
+ gen.next(); // call unarchiveTemplate
1140
+ const error = {}; // no .message
1141
+ gen.throw(error); // put UNARCHIVE_TEMPLATE_FAILURE
1142
+ gen.next(); // CapNotification.error runs
1143
+ expect(CapNotification.error).toHaveBeenCalledWith({ message: 'Failed to unarchive template' });
1144
+ });
1145
+
1146
+ it('bulkArchiveTemplatesSaga error with no message property should use fallback string', () => {
1147
+ const { CapNotification } = require('@capillarytech/cap-ui-library');
1148
+ const gen = bulkArchiveTemplatesSaga({ channel: 'EMAIL', ids: ['id1'] });
1149
+ gen.next(); // call bulkArchiveTemplates
1150
+ const error = {}; // no .message
1151
+ gen.throw(error); // put BULK_ARCHIVE_FAILURE
1152
+ gen.next(); // CapNotification.error runs
1153
+ expect(CapNotification.error).toHaveBeenCalledWith({ message: 'Failed to archive templates' });
1154
+ });
1155
+
1156
+ it('bulkArchiveTemplatesSaga with no successMessage stores undefined successMessage in BULK_ARCHIVE_SUCCESS', () => {
1157
+ const gen = bulkArchiveTemplatesSaga({ channel: 'EMAIL', ids: ['id1', 'id2'] });
1158
+ gen.next(); // call bulkArchiveTemplates
1159
+ const successStep = gen.next({ response: { modifiedCount: 2 } });
1160
+ // successMessage is undefined (not provided); component will use fallback
1161
+ expect(successStep.value).toEqual(put({ type: types.BULK_ARCHIVE_SUCCESS, successMessage: undefined, count: 2 }));
1162
+ });
1163
+
1164
+ it('bulkArchiveTemplatesSaga with no modifiedCount in response falls back to ids.length', () => {
1165
+ const gen = bulkArchiveTemplatesSaga({ channel: 'EMAIL', ids: ['id1', 'id2', 'id3'] });
1166
+ gen.next(); // call bulkArchiveTemplates
1167
+ const successStep = gen.next({ response: {} }); // no modifiedCount
1168
+ expect(successStep.value).toEqual(put({ type: types.BULK_ARCHIVE_SUCCESS, successMessage: undefined, count: 3 }));
1169
+ });
1170
+
1171
+ it('bulkUnarchiveTemplatesSaga error with no message property should use fallback string', () => {
1172
+ const { CapNotification } = require('@capillarytech/cap-ui-library');
1173
+ const gen = bulkUnarchiveTemplatesSaga({ channel: 'EMAIL', ids: ['id1'] });
1174
+ gen.next(); // call bulkUnarchiveTemplates
1175
+ const error = {}; // no .message
1176
+ gen.throw(error); // put BULK_UNARCHIVE_FAILURE
1177
+ gen.next(); // CapNotification.error runs
1178
+ expect(CapNotification.error).toHaveBeenCalledWith({ message: 'Failed to unarchive templates' });
1179
+ });
1180
+
1181
+ it('bulkUnarchiveTemplatesSaga with no successMessage stores undefined successMessage in BULK_UNARCHIVE_SUCCESS', () => {
1182
+ const gen = bulkUnarchiveTemplatesSaga({ channel: 'EMAIL', ids: ['id1', 'id2'] });
1183
+ gen.next(); // call bulkUnarchiveTemplates
1184
+ const successStep = gen.next({ response: { modifiedCount: 2 } });
1185
+ expect(successStep.value).toEqual(put({ type: types.BULK_UNARCHIVE_SUCCESS, successMessage: undefined, count: 2 }));
1186
+ });
1187
+
1188
+ it('bulkUnarchiveTemplatesSaga with no modifiedCount in response falls back to ids.length', () => {
1189
+ const gen = bulkUnarchiveTemplatesSaga({ channel: 'EMAIL', ids: ['id1', 'id2', 'id3'] });
1190
+ gen.next(); // call bulkUnarchiveTemplates
1191
+ const successStep = gen.next({ response: {} }); // no modifiedCount
1192
+ expect(successStep.value).toEqual(put({ type: types.BULK_UNARCHIVE_SUCCESS, successMessage: undefined, count: 3 }));
1193
+ });
1194
+ });
1195
+
1120
1196
  describe('watchGetSenderDetails', () => {
1121
1197
  it('should take latest GET_SENDER_DETAILS_REQUEST', () => {
1122
1198
  const gen = watchGetSenderDetails();
@@ -13,7 +13,7 @@ export default css`
13
13
  max-width: 1140px;
14
14
  margin: 0 auto;
15
15
  width: 100%;
16
- padding: ${CAP_SPACE_24} 0;
16
+ padding: 0.714rem 0;
17
17
  /* Only main channel tabs content, not HTML Editor validation panel tabs */
18
18
  > .cap-tab-v2 > .ant-tabs-content-holder > .ant-tabs-content,
19
19
  > .cap-tab-v2 > .ant-tabs-content {