@capillarytech/creatives-library 8.0.353-alpha.6 → 8.0.354

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 (127) hide show
  1. package/constants/unified.js +0 -29
  2. package/index.html +1 -0
  3. package/package.json +1 -1
  4. package/services/tests/api.test.js +20 -35
  5. package/utils/cdnTransformation.js +63 -3
  6. package/utils/commonUtils.js +1 -19
  7. package/utils/tests/cdnTransformation.test.js +111 -0
  8. package/v2Components/CapActionButton/constants.js +0 -7
  9. package/v2Components/CapActionButton/index.js +108 -166
  10. package/v2Components/CapActionButton/index.scss +6 -157
  11. package/v2Components/CapActionButton/messages.js +3 -19
  12. package/v2Components/CapActionButton/tests/index.test.js +17 -41
  13. package/v2Components/CapTagList/index.js +0 -10
  14. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +49 -72
  15. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
  16. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -213
  17. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
  18. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +10 -85
  19. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
  20. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -79
  21. package/v2Components/CommonTestAndPreview/SendTestMessage.js +5 -10
  22. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +15 -157
  23. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +76 -346
  24. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +4 -133
  25. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +0 -11
  26. package/v2Components/CommonTestAndPreview/constants.js +2 -38
  27. package/v2Components/CommonTestAndPreview/index.js +186 -691
  28. package/v2Components/CommonTestAndPreview/messages.js +3 -45
  29. package/v2Components/CommonTestAndPreview/sagas.js +6 -25
  30. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +284 -308
  31. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +65 -231
  32. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +5 -118
  33. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -341
  34. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +1 -8
  35. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +13 -34
  36. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +283 -281
  37. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
  38. package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -132
  39. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +26 -36
  40. package/v2Components/FormBuilder/index.js +6 -11
  41. package/v2Components/TemplatePreview/_templatePreview.scss +23 -38
  42. package/v2Components/TemplatePreview/index.js +31 -143
  43. package/v2Components/TemplatePreview/tests/index.test.js +0 -142
  44. package/v2Components/TestAndPreviewSlidebox/index.js +1 -13
  45. package/v2Components/TestAndPreviewSlidebox/sagas.js +4 -11
  46. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +1 -3
  47. package/v2Containers/CreativesContainer/SlideBoxContent.js +4 -36
  48. package/v2Containers/CreativesContainer/SlideBoxFooter.js +1 -10
  49. package/v2Containers/CreativesContainer/SlideBoxHeader.js +4 -29
  50. package/v2Containers/CreativesContainer/constants.js +0 -9
  51. package/v2Containers/CreativesContainer/index.js +103 -322
  52. package/v2Containers/CreativesContainer/index.scss +1 -51
  53. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -78
  54. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
  55. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
  56. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +98 -357
  57. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +15 -20
  58. package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
  59. package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
  60. package/v2Containers/Rcs/constants.js +10 -119
  61. package/v2Containers/Rcs/index.js +818 -2450
  62. package/v2Containers/Rcs/index.scss +8 -280
  63. package/v2Containers/Rcs/messages.js +3 -34
  64. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +70073 -98018
  65. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +5 -0
  66. package/v2Containers/Rcs/tests/index.test.js +121 -152
  67. package/v2Containers/Rcs/tests/mockData.js +0 -38
  68. package/v2Containers/Rcs/tests/utils.test.js +30 -646
  69. package/v2Containers/Rcs/utils.js +11 -478
  70. package/v2Containers/Sms/Create/index.js +40 -106
  71. package/v2Containers/SmsTrai/Create/index.js +4 -9
  72. package/v2Containers/SmsTrai/Edit/constants.js +0 -2
  73. package/v2Containers/SmsTrai/Edit/index.js +130 -640
  74. package/v2Containers/SmsTrai/Edit/messages.js +4 -14
  75. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2296 -4249
  76. package/v2Containers/SmsWrapper/index.js +8 -37
  77. package/v2Containers/TagList/index.js +0 -6
  78. package/v2Containers/Templates/_templates.scss +9 -166
  79. package/v2Containers/Templates/actions.js +0 -11
  80. package/v2Containers/Templates/constants.js +0 -2
  81. package/v2Containers/Templates/index.js +52 -120
  82. package/v2Containers/Templates/sagas.js +18 -57
  83. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1017 -1062
  84. package/v2Containers/Templates/tests/sagas.test.js +39 -205
  85. package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -72
  86. package/v2Containers/TemplatesV2/index.js +23 -86
  87. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
  88. package/v2Containers/Whatsapp/index.js +20 -3
  89. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -578
  90. package/utils/rcsPayloadUtils.js +0 -92
  91. package/utils/templateVarUtils.js +0 -201
  92. package/utils/tests/rcsPayloadUtils.test.js +0 -226
  93. package/utils/tests/templateVarUtils.test.js +0 -204
  94. package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
  95. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
  96. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -91
  97. package/v2Components/SmsFallback/constants.js +0 -73
  98. package/v2Components/SmsFallback/index.js +0 -956
  99. package/v2Components/SmsFallback/index.scss +0 -265
  100. package/v2Components/SmsFallback/messages.js +0 -78
  101. package/v2Components/SmsFallback/smsFallbackUtils.js +0 -119
  102. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
  103. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
  104. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
  105. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -223
  106. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -309
  107. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
  108. package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
  109. package/v2Components/TemplatePreview/constants.js +0 -2
  110. package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
  111. package/v2Components/VarSegmentMessageEditor/index.js +0 -125
  112. package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
  113. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -43
  114. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -79
  115. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
  116. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
  117. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
  118. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -225
  119. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -318
  120. package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
  121. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
  122. package/v2Containers/SmsTrai/Edit/index.scss +0 -121
  123. package/v2Containers/Templates/TemplatesActionBar.js +0 -101
  124. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
  125. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
  126. package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
  127. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +0 -131
@@ -1,22 +1,20 @@
1
1
  import { expectSaga } from 'redux-saga-test-plan';
2
- import {
3
- take, call, takeLatest, takeEvery, put,
4
- } from 'redux-saga/effects';
5
2
  import * as matchers from 'redux-saga-test-plan/matchers';
6
3
  import { throwError } from 'redux-saga-test-plan/providers';
4
+ import { call, takeLatest, put } from 'redux-saga/effects';
7
5
  import * as api from '../../../services/api';
8
6
  import * as types from '../constants';
9
7
  import * as cdnUtils from '../../../utils/cdnTransformation';
10
8
  import {
11
9
  getCdnTransformationConfig,
12
10
  getAllTemplates,
13
- getLocalSmsTemplates,
14
11
  watchGetCdnTransformationConfig,
15
12
  watchGetAllTemplates,
16
13
  getSenderDetails,
17
14
  fetchWeCrmAccounts,
18
15
  sendZippedFile,
19
16
  fetchUserList,
17
+ watchGetUserList,
20
18
  deleteRcsTemplate,
21
19
  watchDeleteRcsTemplate,
22
20
  watchForGetTemplateInfoById,
@@ -41,14 +39,10 @@ import {
41
39
  watchUnarchiveTemplate,
42
40
  watchBulkArchive,
43
41
  watchBulkUnarchive,
44
- watchGetUserList,
45
42
  } from '../sagas';
46
-
47
- import * as mockData from './mockData';
48
- import { fetchSmsTemplatesFromQuery } from '../utils/smsTemplatesListApi';
49
- import { ZALO } from '../../CreativesContainer/constants';
50
- import { VIET_GUYS, ZALO_TEMPLATE_INFO_REQUEST } from '../../Zalo/constants';
51
43
  import { getTemplateInfoById } from '../../Zalo/saga';
44
+ import { ZALO_TEMPLATE_INFO_REQUEST } from '../../Zalo/constants';
45
+ import * as mockData from './mockData';
52
46
 
53
47
  jest.mock('@capillarytech/cap-ui-library', () => ({
54
48
  CapNotification: {
@@ -60,10 +54,25 @@ jest.mock('@capillarytech/cap-ui-library', () => ({
60
54
  }));
61
55
 
62
56
  describe('getCdnTransformationConfig saga', () => {
63
- it("handle valid response from api", () => {
57
+ afterEach(() => {
58
+ delete window.APP_ENV;
59
+ });
60
+
61
+ it("short-circuits to env config and skips the API call when window.APP_ENV is set", async () => {
62
+ const initSpy = jest.spyOn(cdnUtils, "initCdnConfigFromEnv").mockReturnValue(true);
63
+ const apiSpy = jest.spyOn(api, "getCdnTransformationConfig");
64
+
65
+ await expectSaga(getCdnTransformationConfig).run();
66
+
67
+ expect(initSpy).toHaveBeenCalled();
68
+ expect(apiSpy).not.toHaveBeenCalled();
69
+ });
70
+
71
+ it("handle valid response from api", async () => {
72
+ jest.spyOn(cdnUtils, "initCdnConfigFromEnv").mockReturnValue(false);
64
73
  const saveCdnConfigsSpy = jest.spyOn(cdnUtils, "saveCdnConfigs");
65
74
 
66
- expectSaga(getCdnTransformationConfig)
75
+ await expectSaga(getCdnTransformationConfig)
67
76
  .provide([
68
77
  [
69
78
  call(api.getCdnTransformationConfig),
@@ -74,13 +83,14 @@ describe('getCdnTransformationConfig saga', () => {
74
83
  expect(saveCdnConfigsSpy).toHaveBeenCalled();
75
84
  });
76
85
 
77
- it("handle error response from api", () => {
86
+ it("handle error response from api", async () => {
87
+ jest.spyOn(cdnUtils, "initCdnConfigFromEnv").mockReturnValue(false);
78
88
  const removeAllCdnLocalStorageItemsSpy = jest.spyOn(
79
89
  cdnUtils,
80
90
  "removeAllCdnLocalStorageItems"
81
91
  );
82
92
 
83
- expectSaga(getCdnTransformationConfig)
93
+ await expectSaga(getCdnTransformationConfig)
84
94
  .provide([
85
95
  [
86
96
  call(api.getCdnTransformationConfig),
@@ -91,7 +101,8 @@ describe('getCdnTransformationConfig saga', () => {
91
101
  expect(removeAllCdnLocalStorageItemsSpy).toHaveBeenCalled();
92
102
  });
93
103
 
94
- it("remove localStorage items when an error is thrown", () => {
104
+ it("remove localStorage items when an error is thrown", async () => {
105
+ jest.spyOn(cdnUtils, "initCdnConfigFromEnv").mockReturnValue(false);
95
106
  const saveCdnConfigsSpy = jest.spyOn(cdnUtils, "saveCdnConfigs").mockImplementation(()=>{
96
107
  throw new Error()
97
108
  });
@@ -100,7 +111,7 @@ describe('getCdnTransformationConfig saga', () => {
100
111
  "removeAllCdnLocalStorageItems"
101
112
  );
102
113
 
103
- expectSaga(getCdnTransformationConfig)
114
+ await expectSaga(getCdnTransformationConfig)
104
115
  .provide([
105
116
  [
106
117
  call(api.getCdnTransformationConfig),
@@ -156,8 +167,8 @@ describe('templateList saga', () => {
156
167
  ],
157
168
  ]).put({
158
169
  type: types.GET_ALL_TEMPLATES_SUCCESS,
159
- data: mockData.getAllTemplatesListSuccess.response,
160
- weCRMTemplate: mockData.getAllTemplatesListSuccess.response?.unMapped,
170
+ data: mockData.getAllTemplatesListSuccess,
171
+ weCRMTemplate: mockData.getAllTemplatesListSuccess,
161
172
  isReset: mockData.getAllTemplatesListSuccess?.queryParams?.page === 1,
162
173
  })
163
174
  .run();
@@ -173,8 +184,8 @@ describe('templateList saga', () => {
173
184
  ],
174
185
  ]).put({
175
186
  type: types.GET_ALL_TEMPLATES_SUCCESS,
176
- data: mockData.getAllTemplatesListSuccess.response,
177
- weCRMTemplate: mockData.getAllTemplatesListSuccess.response?.unMapped,
187
+ data: mockData.getAllTemplatesListSuccess,
188
+ weCRMTemplate: mockData.getAllTemplatesListSuccess,
178
189
  isReset: mockData.getAllTemplatesListSuccess?.queryParams?.page === 1,
179
190
  })
180
191
  .run();
@@ -193,163 +204,6 @@ describe('templateList saga', () => {
193
204
  })
194
205
  .run();
195
206
  });
196
-
197
- it('SMS channel uses fetchSmsTemplatesFromQuery', () => {
198
- const smsAction = {
199
- type: types.GET_ALL_TEMPLATES_REQUEST,
200
- channel: 'Sms',
201
- queryParams: { page: 1, perPage: 25, name: '', sortBy: 'Most Recent' },
202
- intlCopyOf: 'Kopie',
203
- };
204
- const fetched = {
205
- channelTemplates: { templates: [{ name: 'Kopie Foo' }], totalCount: 1 },
206
- weCRMTemplate: undefined,
207
- raw: {},
208
- };
209
- return expectSaga(getAllTemplates, smsAction)
210
- .provide([
211
- [call(fetchSmsTemplatesFromQuery, smsAction.queryParams, smsAction.intlCopyOf), fetched],
212
- ])
213
- .put({
214
- type: types.GET_ALL_TEMPLATES_SUCCESS,
215
- data: fetched.channelTemplates,
216
- weCRMTemplate: fetched.weCRMTemplate,
217
- isReset: true,
218
- })
219
- .run();
220
- });
221
-
222
- it('SMS channel propagates fetchSmsTemplatesFromQuery errors', () => {
223
- const smsAction = {
224
- type: types.GET_ALL_TEMPLATES_REQUEST,
225
- channel: 'SMS',
226
- queryParams: { page: 2, perPage: 25 },
227
- intlCopyOf: '',
228
- };
229
- const error = new Error('sms list fail');
230
- return expectSaga(getAllTemplates, smsAction)
231
- .provide([
232
- [call(fetchSmsTemplatesFromQuery, smsAction.queryParams, smsAction.intlCopyOf), throwError(error)],
233
- ])
234
- .put({
235
- type: types.GET_ALL_TEMPLATES_FAILURE,
236
- error,
237
- })
238
- .run();
239
- });
240
-
241
- it('wechat channel merges mapped and richmedia and applies Most Recent sort', () => {
242
- const wechatApiResponse = {
243
- response: {
244
- mapped: [
245
- { name: 'B', updatedAt: '2024-01-02T00:00:00Z' },
246
- { name: 'A', updatedAt: '2024-01-01T00:00:00Z' },
247
- ],
248
- richmedia: [{ name: 'C', updatedAt: '2024-01-03T00:00:00Z' }],
249
- unMapped: [],
250
- },
251
- };
252
- const action = {
253
- channel: 'wechat',
254
- queryParams: { sortBy: 'Most Recent', page: 1 },
255
- intlCopyOf: '',
256
- };
257
- return expectSaga(getAllTemplates, action)
258
- .provide([[matchers.call.fn(api.getAllTemplates), wechatApiResponse]])
259
- .put.actionType(types.GET_ALL_TEMPLATES_SUCCESS)
260
- .run();
261
- });
262
-
263
- it('wechat channel applies Alphabetically sort', () => {
264
- const wechatApiResponse = {
265
- response: {
266
- mapped: [{ name: 'Zebra', updatedAt: '2024-01-02T00:00:00Z' }],
267
- richmedia: [{ name: 'Apple', updatedAt: '2024-01-01T00:00:00Z' }],
268
- unMapped: [],
269
- },
270
- };
271
- const action = {
272
- channel: 'wechat',
273
- queryParams: { sortBy: 'Alphabetically', page: 1 },
274
- intlCopyOf: '',
275
- };
276
- return expectSaga(getAllTemplates, action)
277
- .provide([[matchers.call.fn(api.getAllTemplates), wechatApiResponse]])
278
- .put.actionType(types.GET_ALL_TEMPLATES_SUCCESS)
279
- .run();
280
- });
281
-
282
- it('wechat channel replaces Copy-of prefix when intlCopyOf is set', () => {
283
- const wechatApiResponse = {
284
- response: {
285
- mapped: [{ name: 'Copy of Promo', updatedAt: '2024-01-01T00:00:00Z' }],
286
- richmedia: [],
287
- unMapped: [],
288
- },
289
- };
290
- const action = {
291
- channel: 'wechat',
292
- queryParams: { page: 1 },
293
- intlCopyOf: 'Kopia av ',
294
- };
295
- return expectSaga(getAllTemplates, action)
296
- .provide([[matchers.call.fn(api.getAllTemplates), wechatApiResponse]])
297
- .put.actionType(types.GET_ALL_TEMPLATES_SUCCESS)
298
- .run();
299
- });
300
- });
301
-
302
- describe('getLocalSmsTemplates saga', () => {
303
- it('invokes onSuccess with fetched data', () => {
304
- const onSuccess = jest.fn();
305
- const action = {
306
- queryParams: { page: 1 },
307
- intlCopyOf: '',
308
- onSuccess,
309
- };
310
- const fetched = { channelTemplates: { templates: [] }, raw: {} };
311
- return expectSaga(getLocalSmsTemplates, action)
312
- .provide([[call(fetchSmsTemplatesFromQuery, action.queryParams, action.intlCopyOf), fetched]])
313
- .call(onSuccess, fetched)
314
- .run();
315
- });
316
-
317
- it('invokes onFailure when fetch throws', () => {
318
- const onFailure = jest.fn();
319
- const error = new Error('local list fail');
320
- const action = {
321
- queryParams: { page: 1 },
322
- intlCopyOf: 'Kopie',
323
- onFailure,
324
- };
325
- return expectSaga(getLocalSmsTemplates, action)
326
- .provide([
327
- [call(fetchSmsTemplatesFromQuery, action.queryParams, action.intlCopyOf), throwError(error)],
328
- ])
329
- .call(onFailure, error)
330
- .run();
331
- });
332
-
333
- it('does not throw when callbacks are omitted', () => {
334
- const action = { queryParams: { page: 1 }, intlCopyOf: '' };
335
- return expectSaga(getLocalSmsTemplates, action)
336
- .provide([
337
- [call(fetchSmsTemplatesFromQuery, action.queryParams, action.intlCopyOf), throwError(new Error('x'))],
338
- ])
339
- .run();
340
- });
341
-
342
- it('does not call onSuccess when it is not a function', () => {
343
- const action = {
344
- queryParams: { page: 1 },
345
- intlCopyOf: '',
346
- onSuccess: 'not-a-fn',
347
- };
348
- const fetched = { channelTemplates: { templates: [] } };
349
- return expectSaga(getLocalSmsTemplates, action)
350
- .provide([[call(fetchSmsTemplatesFromQuery, action.queryParams, action.intlCopyOf), fetched]])
351
- .run();
352
- });
353
207
  });
354
208
 
355
209
  describe('watchForTemplates saga', () => {
@@ -543,7 +397,7 @@ describe('getSenderDetails Saga', () => {
543
397
 
544
398
  describe('fetchWeCrmAccounts Saga', () => {
545
399
  const action = { source: 'CRM' };
546
- it('handles successful fetching of WeCRM accounts', () => {
400
+ test.concurrent('handles successful fetching of WeCRM accounts', () => {
547
401
  const fakeResponse = {
548
402
  response: [{ id: 1, name: 'Account One' }]
549
403
  };
@@ -556,11 +410,14 @@ describe('fetchWeCrmAccounts Saga', () => {
556
410
  })
557
411
  );
558
412
  });
559
- it('handles error thrown from api', () => {
413
+ test.concurrent('handles error thrown from api', () => {
560
414
  const error = new Error('Fetch failed');
561
- expectSaga(fetchWeCrmAccounts, action)
415
+ expectSaga(fetchWeCrmAccounts, action.source)
562
416
  .provide([
563
- [call(api.fetchWeCrmAccounts, action.source), throwError(error)],
417
+ [
418
+ call(api.fetchWeCrmAccounts),
419
+ throwError(error)
420
+ ],
564
421
  ])
565
422
  .put({
566
423
  type: types.GET_WECRM_ACCOUNTS_FAILURE,
@@ -602,29 +459,6 @@ describe('sendZippedFile Saga', () => {
602
459
  expect(generator.next().value).toEqual(call(api.sendZippedFile, action.selectedFile));
603
460
  });
604
461
 
605
- it('calls errorHandler with message and returns early when isError is true', () => {
606
- const errorHandler = jest.fn();
607
- const successHandler = jest.fn();
608
- const errorAction = {
609
- selectedFile: 'upload.zip',
610
- errorHandler,
611
- successHandler,
612
- };
613
- const fakeErrorResponse = {
614
- status: { isError: true },
615
- message: 'Upload failed',
616
- };
617
- const generator = sendZippedFile(errorAction);
618
- expect(generator.next().value).toEqual(call(api.sendZippedFile, errorAction.selectedFile));
619
- // Advancing with isError response causes the saga to call errorHandler and yield
620
- const yieldStep = generator.next(fakeErrorResponse);
621
- expect(errorHandler).toHaveBeenCalledWith('Upload failed');
622
- expect(yieldStep.done).toBe(false);
623
- // After resuming from the yield, the return statement completes the generator
624
- expect(generator.next().done).toBe(true);
625
- expect(successHandler).not.toHaveBeenCalled();
626
- });
627
-
628
462
  it('handles error thrown from api', () => {
629
463
  const error = new Error('Fetch failed');
630
464
  expectSaga(sendZippedFile, action)
@@ -1390,7 +1224,7 @@ describe('v2TemplateSaga', () => {
1390
1224
  // all() returns an IO object with an ALL key containing the array of effects
1391
1225
  expect(step.value).toHaveProperty('@@redux-saga/IO', true);
1392
1226
  expect(step.value).toHaveProperty('ALL');
1393
- expect(step.value.ALL).toHaveLength(15);
1227
+ expect(step.value.ALL).toHaveLength(14);
1394
1228
  expect(step.done).toBe(false);
1395
1229
  });
1396
1230
  });
@@ -10,7 +10,7 @@ export default css`
10
10
 
11
11
  .component-wrapper {
12
12
  ${(props) => props.isFullMode ? `
13
- max-width: 71.25rem;
13
+ max-width: 1140px;
14
14
  margin: 0 auto;
15
15
  width: 100%;
16
16
  padding: 0.714rem 0;
@@ -23,77 +23,6 @@ export default css`
23
23
  height: calc(100vh - 11.25rem);
24
24
  } `}
25
25
  }
26
-
27
- /* SMS fallback / local list: single pane skips .cap-tab-v2, so flex-fill the grid instead of viewport-fixed pagination height */
28
- .creatives-templates-container--local-sms.library-mode {
29
- display: flex;
30
- flex-direction: column;
31
- flex: 1;
32
- min-height: 0;
33
- height: 100%;
34
-
35
- .component-wrapper {
36
- display: flex;
37
- flex-direction: column;
38
- flex: 1;
39
- min-height: 0;
40
- height: 100%;
41
- }
42
-
43
- .templates-v2-local-sms-pane {
44
- display: flex;
45
- flex-direction: column;
46
- flex: 1;
47
- min-height: 0;
48
- overflow: hidden;
49
- height: 100%;
50
- }
51
-
52
- .templates-v2-local-sms-pane .creatives-templates-list.library-mode {
53
- display: flex;
54
- flex-direction: column;
55
- flex: 1;
56
- min-height: 0;
57
- overflow: hidden;
58
- height: 100%;
59
- }
60
-
61
- .templates-v2-local-sms-pane .creatives-templates-list.library-mode > .cap-row:first-of-type {
62
- flex: 1;
63
- min-height: 0;
64
- display: flex;
65
- flex-direction: column;
66
- overflow: hidden;
67
- }
68
-
69
- .templates-v2-local-sms-pane .creatives-templates-list.library-mode > .cap-row:first-of-type > div {
70
- flex: 1;
71
- min-height: 0;
72
- display: flex;
73
- flex-direction: column;
74
- overflow: hidden;
75
- }
76
-
77
- .templates-v2-local-sms-pane .creatives-templates-list.library-mode > .cap-row:first-of-type > div > div:first-child {
78
- flex: 1;
79
- min-height: 0;
80
- display: flex;
81
- flex-direction: column;
82
- overflow: hidden;
83
- }
84
-
85
- .templates-v2-local-sms-pane .v2-pagination-container,
86
- .templates-v2-local-sms-pane .v2-pagination-container-half {
87
- /* Match _templates local-SMS: bounded height so overflow-y scroll works (flex+100% alone often doesn’t) */
88
- flex: 0 1 auto;
89
- min-height: 0;
90
- height: calc(100vh - 12rem);
91
- max-height: calc(100vh - 12rem);
92
- overflow-y: auto;
93
- overflow-x: hidden;
94
- -webkit-overflow-scrolling: touch;
95
- }
96
- }
97
26
  `;
98
27
 
99
28
  export const CapTabStyle = css`
@@ -10,8 +10,8 @@ import { connect } from 'react-redux';
10
10
  import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
11
11
  import { createStructuredSelector } from 'reselect';
12
12
  import { bindActionCreators, compose } from 'redux';
13
- import { CapTab, CapCustomCard, CapButton, CapHeader, CapIcon, CapSpin, CapTooltip } from '@capillarytech/cap-ui-library';
14
- import { find, get, pick } from 'lodash';
13
+ import { CapTab, CapCustomCard, CapButton, CapHeader, CapSpin, CapIcon, CapTooltip } from '@capillarytech/cap-ui-library';
14
+ import { find, get } from 'lodash';
15
15
  import Helmet from 'react-helmet';
16
16
 
17
17
  import { UserIsAuthenticated } from '../../utils/authWrapper';
@@ -36,14 +36,13 @@ import { makeSelectAuthenticated, selectCurrentOrgDetails } from "../../v2Contai
36
36
  import {
37
37
  CALL_TASK,
38
38
  COMMON_CHANNELS,
39
- LOCAL_TEMPLATE_CONFIG_KEYS_FOR_PICK,
40
39
  LOYALTY_SUPPORTED_ACTION,
41
40
  MOBILE_PUSH,
42
41
  NORMALIZED_CHANNEL_ALIASES,
43
42
  SMS,
44
43
  } from "../CreativesContainer/constants";
45
44
 
46
- const { CapCustomCardList } = CapCustomCard;
45
+ const {CapCustomCardList} = CapCustomCard;
47
46
 
48
47
  const StyledCapTab = withStyles(CapTab, CapTabStyle);
49
48
  export class TemplatesV2 extends React.Component { // eslint-disable-line react/prefer-stateless-function
@@ -120,9 +119,9 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
120
119
  return !normalizedChannelsToHideSet.has(paneKey);
121
120
  });
122
121
 
123
- if (isFullMode && !normalizedChannelsToHideSet.has(normalizeChannel(ASSETS))) {
124
- filteredPanes.push({ content: <div></div>, tab: intl.formatMessage(messages.gallery), key: ASSETS });
125
- } else if (!isFullMode) {
122
+ if (isFullMode) {
123
+ filteredPanes.push({ content: <div></div>, tab: intl.formatMessage(messages.gallery), key: 'assets' });
124
+ } else {
126
125
  // Add special-mode panes only when not hidden (use normalized checks)
127
126
  if (!normalizedChannelsToHideSet.has(CALL_TASK.toLowerCase())) {
128
127
  filteredPanes.push({ content: <div></div>, tab: intl.formatMessage(messages.callTask), key: CALL_TASK.toLowerCase() });
@@ -223,8 +222,7 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
223
222
  this.setState({selectedChannel: nextProps.channel, panes });
224
223
  }
225
224
  }
226
-
227
- getTemplateDataForGrid = ({ templates, handlers, filterContent, channel, isLoading, loadingTip }) => {
225
+ getTemplateDataForGrid = ({templates, handlers, filterContent, channel, isLoading, loadingTip}) => {
228
226
  const currentChannel = channel.toUpperCase();
229
227
  const cardDataList = templates.map((template) => {
230
228
  const templateData =
@@ -250,8 +248,7 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
250
248
  </CapSpin>
251
249
 
252
250
  </div>);
253
- };
254
-
251
+ }
255
252
  getGalleryComponent = (location) => <Gallery location={location} isFullMode={this.props.isFullMode}/>
256
253
  getCallTaskComponent = () => (
257
254
  <CallTask
@@ -315,29 +312,6 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
315
312
  if (messageStrategy !== "X_ENGAGE" && channel === 'facebook' && !isFullMode) {
316
313
  return this.getFacebookComponent();
317
314
  }
318
- const localConfig = this.props.localTemplatesConfig || pick(this.props, LOCAL_TEMPLATE_CONFIG_KEYS_FOR_PICK);
319
- const useLocalTemplates = localConfig.useLocalTemplates;
320
- if (useLocalTemplates && channel === (this.props.channel || 'sms')) {
321
- // Reuse full Templates component (same UI as Redux flow) with local data only
322
- const location = { pathname: `/${channel}`, search: '', query: !this.props.isFullMode ? { type: 'embedded', module: 'library' } : {} };
323
- return (
324
- <Templates
325
- key={`${channel}-local`}
326
- location={location}
327
- route={{ name: channel }}
328
- router={this.props.router}
329
- isFullMode={this.props.isFullMode}
330
- createNew={this.props.createNew}
331
- onSelectTemplate={this.props.onSelectTemplate}
332
- handlePeviewTemplate={this.props.handlePeviewTemplate}
333
- messageStrategy={this.props.messageStrategy}
334
- smsRegister={this.props.smsRegister}
335
- hideTestAndPreviewBtn={this.props.hideTestAndPreviewBtn}
336
- localTemplatesConfig={localConfig}
337
- />
338
- );
339
- }
340
-
341
315
  const location = {pathname: `/${channel}`, search: '', query};
342
316
  switch (channel) {
343
317
  case 'call_task':
@@ -387,55 +361,29 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
387
361
  }
388
362
  render() {
389
363
  const { isFullMode, className, cap = {}, Global = {}} = this.props;
390
- const useLocalTemplates = get(this.props, 'localTemplatesConfig.useLocalTemplates', false);
391
364
  const { accessiblePermissions = []} = cap.user || Global.user || {};
392
365
  let isCreativeAccessible = true;
393
366
  if (!accessiblePermissions.includes(CREATIVES_UI_VIEW)) {
394
367
  isCreativeAccessible = false;
395
368
  }
396
- // Recompute active pane content every render so local-list mode updates
397
- // (templates/loading/search UI) are not stuck with the initial cached pane.
398
- const panes = this.setChannelContent(this.state.selectedChannel, this.state.panes);
399
- const hideChannelTabsForLocalSms = useLocalTemplates && panes.length === 1;
400
- const activeLocalPane = hideChannelTabsForLocalSms
401
- ? (panes.find(
402
- (p) => String(p.key).toLowerCase() === String(this.state.selectedChannel).toLowerCase(),
403
- ) || panes[0])
404
- : null;
405
369
  return (
406
370
  !isCreativeAccessible ? <AccessForbidden /> : (
407
- <div
408
- className={`${className} creatives-templates-container ${isFullMode ? 'fullmode' : 'library-mode'}${useLocalTemplates ? ' creatives-templates-container--local-sms' : ''}`}
409
- data-testid="cap-wrapper"
410
- >
411
- {isFullMode && !useLocalTemplates && (
412
- <Helmet
413
- title={this.props.intl.formatMessage(messages.creatives)}
414
- meta={[
415
- { name: 'description', content: this.props.intl.formatMessage(messages.creativesDesc) },
416
- ]}
417
- />
418
- )}
371
+ <div className={`${className} creatives-templates-container ${isFullMode ? 'fullmode' : 'library-mode'}`} data-testid="cap-wrapper">
372
+ {isFullMode && <Helmet
373
+ title={this.props.intl.formatMessage(messages.creatives)}
374
+ meta={[
375
+ { name: 'description', content: this.props.intl.formatMessage(messages.creativesDesc) },
376
+ ]}
377
+ />}
419
378
  <div className="component-wrapper">
420
- {isFullMode && (
421
- <CapHeader
422
- title={<FormattedMessage {...messages.creatives} />}
423
- {...(!useLocalTemplates && {
424
- description: <FormattedMessage {...messages.creativesDesc} />,
425
- })}
426
- />
427
- )}
428
- {hideChannelTabsForLocalSms ? (
429
- <div className="templates-v2-local-sms-pane">{activeLocalPane?.content}</div>
430
- ) : (
431
- <StyledCapTab
432
- panes={panes}
433
- onChange={this.channelChange}
434
- activeKey={this.state.selectedChannel}
435
- defaultActiveKey={this.state.selectedChannel}
436
- isFullMode={isFullMode}
437
- />
438
- )}
379
+ {isFullMode && <CapHeader title={<FormattedMessage {...messages.creatives}/>} description={<FormattedMessage {...messages.creativesDesc}/>}/>}
380
+ <StyledCapTab
381
+ panes={this.state.panes}
382
+ onChange={this.channelChange}
383
+ activeKey={this.state.selectedChannel}
384
+ defaultActiveKey={this.state.selectedChannel}
385
+ isFullMode={isFullMode}
386
+ />
439
387
  </div>
440
388
  </div>
441
389
  )
@@ -467,17 +415,6 @@ TemplatesV2.propTypes = {
467
415
  currentOrgDetails: PropTypes.object,
468
416
  restrictPersonalization: PropTypes.bool,
469
417
  isAnonymousType: PropTypes.bool,
470
- // Optional: reuse grid UI with local template list (e.g. SMS fallback). Pass object or same keys as individual props.
471
- localTemplatesConfig: PropTypes.shape({
472
- useLocalTemplates: PropTypes.bool,
473
- localTemplates: PropTypes.arrayOf(PropTypes.object),
474
- localTemplatesLoading: PropTypes.bool,
475
- localTemplatesLoadingTip: PropTypes.string,
476
- localTemplatesFilterContent: PropTypes.node,
477
- localTemplatesFooterContent: PropTypes.node,
478
- localTemplatesOnPageChange: PropTypes.func,
479
- localTemplatesUseSkeleton: PropTypes.bool,
480
- }),
481
418
  };
482
419
 
483
420
  TemplatesV2.defaultProps = {