@capillarytech/creatives-library 9.0.15-alpha.3 → 9.0.16-beta.0

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 (146) hide show
  1. package/AppRoot.js +80 -0
  2. package/app.js +5 -3
  3. package/constants/unified.js +0 -29
  4. package/entry.js +67 -1
  5. package/global-styles.js +1 -1
  6. package/mfe-exposed-components.js +2 -4
  7. package/package.json +2 -2
  8. package/services/api.js +1 -1
  9. package/services/tests/api.test.js +20 -35
  10. package/styles/containers/layout/_layoutPage.scss +8 -6
  11. package/utils/commonUtils.js +1 -19
  12. package/utils/getDataLayer.js +15 -0
  13. package/utils/gtmTrackers/gtmEvents/creativeDetails.js +2 -1
  14. package/utils/mfeDetect.js +1 -0
  15. package/v2Components/CapActionButton/constants.js +1 -8
  16. package/v2Components/CapActionButton/index.js +144 -202
  17. package/v2Components/CapActionButton/index.scss +14 -232
  18. package/v2Components/CapActionButton/messages.js +3 -19
  19. package/v2Components/CapActionButton/tests/index.test.js +18 -42
  20. package/v2Components/CapImageUpload/index.js +2 -2
  21. package/v2Components/CapTagList/index.js +0 -10
  22. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +48 -70
  23. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
  24. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -214
  25. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
  26. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +9 -83
  27. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
  28. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -60
  29. package/v2Components/CommonTestAndPreview/SendTestMessage.js +5 -10
  30. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +22 -202
  31. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +80 -381
  32. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +8 -155
  33. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +0 -11
  34. package/v2Components/CommonTestAndPreview/constants.js +2 -38
  35. package/v2Components/CommonTestAndPreview/index.js +222 -761
  36. package/v2Components/CommonTestAndPreview/messages.js +3 -45
  37. package/v2Components/CommonTestAndPreview/sagas.js +6 -25
  38. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +0 -1
  39. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +267 -284
  40. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +65 -231
  41. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +5 -118
  42. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -341
  43. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +1 -8
  44. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +13 -34
  45. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +283 -281
  46. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
  47. package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -133
  48. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +24 -31
  49. package/v2Components/NavigationBar/index.js +9 -7
  50. package/v2Components/NavigationBar/mfeModuleHeader.config.js +16 -0
  51. package/v2Components/Pagination/_pagination.scss +2 -0
  52. package/v2Components/TemplatePreview/_templatePreview.scss +25 -45
  53. package/v2Components/TemplatePreview/index.js +32 -147
  54. package/v2Components/TemplatePreview/tests/index.test.js +0 -142
  55. package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +6 -6
  56. package/v2Components/TestAndPreviewSlidebox/index.js +1 -13
  57. package/v2Components/TestAndPreviewSlidebox/sagas.js +4 -11
  58. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +1 -3
  59. package/v2Containers/Cap/constants.js +1 -0
  60. package/v2Containers/Cap/index.js +47 -21
  61. package/v2Containers/Cap/sagas.js +19 -0
  62. package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +1 -4
  63. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +0 -4
  64. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +20 -20
  65. package/v2Containers/CreativesContainer/SlideBoxContent.js +7 -37
  66. package/v2Containers/CreativesContainer/SlideBoxFooter.js +5 -14
  67. package/v2Containers/CreativesContainer/SlideBoxHeader.js +5 -36
  68. package/v2Containers/CreativesContainer/constants.js +0 -11
  69. package/v2Containers/CreativesContainer/index.js +104 -323
  70. package/v2Containers/CreativesContainer/index.scss +1 -83
  71. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -79
  72. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
  73. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
  74. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +90 -333
  75. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +15 -20
  76. package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
  77. package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
  78. package/v2Containers/Rcs/constants.js +11 -131
  79. package/v2Containers/Rcs/index.js +829 -2682
  80. package/v2Containers/Rcs/index.scss +8 -443
  81. package/v2Containers/Rcs/messages.js +6 -45
  82. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +39409 -74176
  83. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +74 -10
  84. package/v2Containers/Rcs/tests/index.test.js +40 -151
  85. package/v2Containers/Rcs/tests/mockData.js +0 -38
  86. package/v2Containers/Rcs/tests/utils.test.js +30 -643
  87. package/v2Containers/Rcs/utils.js +12 -466
  88. package/v2Containers/Sms/Create/index.js +48 -115
  89. package/v2Containers/SmsTrai/Create/index.js +4 -9
  90. package/v2Containers/SmsTrai/Edit/constants.js +0 -2
  91. package/v2Containers/SmsTrai/Edit/index.js +169 -678
  92. package/v2Containers/SmsTrai/Edit/messages.js +4 -14
  93. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2854 -5455
  94. package/v2Containers/SmsWrapper/index.js +8 -37
  95. package/v2Containers/TagList/index.js +0 -6
  96. package/v2Containers/Templates/_templates.scss +11 -196
  97. package/v2Containers/Templates/actions.js +0 -11
  98. package/v2Containers/Templates/constants.js +0 -2
  99. package/v2Containers/Templates/index.js +60 -131
  100. package/v2Containers/Templates/sagas.js +13 -57
  101. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1015 -1060
  102. package/v2Containers/Templates/tests/sagas.test.js +16 -199
  103. package/v2Containers/TemplatesV2/TemplatesV2.style.js +10 -75
  104. package/v2Containers/TemplatesV2/index.js +43 -86
  105. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
  106. package/v2Containers/Whatsapp/index.js +25 -4
  107. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -578
  108. package/utils/rcsPayloadUtils.js +0 -92
  109. package/utils/templateVarUtils.js +0 -201
  110. package/utils/tests/rcsPayloadUtils.test.js +0 -226
  111. package/utils/tests/templateVarUtils.test.js +0 -204
  112. package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
  113. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
  114. package/v2Components/CommonTestAndPreview/utils.js +0 -84
  115. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -91
  116. package/v2Components/SmsFallback/constants.js +0 -94
  117. package/v2Components/SmsFallback/index.js +0 -958
  118. package/v2Components/SmsFallback/index.scss +0 -266
  119. package/v2Components/SmsFallback/messages.js +0 -78
  120. package/v2Components/SmsFallback/smsFallbackUtils.js +0 -120
  121. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
  122. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
  123. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
  124. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -208
  125. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -309
  126. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
  127. package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
  128. package/v2Components/TemplatePreview/constants.js +0 -2
  129. package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
  130. package/v2Components/VarSegmentMessageEditor/index.js +0 -125
  131. package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
  132. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -17
  133. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -79
  134. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
  135. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
  136. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
  137. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -227
  138. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -318
  139. package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
  140. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
  141. package/v2Containers/SmsTrai/Edit/index.scss +0 -126
  142. package/v2Containers/Templates/TemplatesActionBar.js +0 -101
  143. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
  144. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
  145. package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
  146. 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: {
@@ -173,8 +167,8 @@ describe('templateList saga', () => {
173
167
  ],
174
168
  ]).put({
175
169
  type: types.GET_ALL_TEMPLATES_SUCCESS,
176
- data: mockData.getAllTemplatesListSuccess.response,
177
- weCRMTemplate: mockData.getAllTemplatesListSuccess.response?.unMapped,
170
+ data: mockData.getAllTemplatesListSuccess,
171
+ weCRMTemplate: mockData.getAllTemplatesListSuccess,
178
172
  isReset: mockData.getAllTemplatesListSuccess?.queryParams?.page === 1,
179
173
  })
180
174
  .run();
@@ -190,8 +184,8 @@ describe('templateList saga', () => {
190
184
  ],
191
185
  ]).put({
192
186
  type: types.GET_ALL_TEMPLATES_SUCCESS,
193
- data: mockData.getAllTemplatesListSuccess.response,
194
- weCRMTemplate: mockData.getAllTemplatesListSuccess.response?.unMapped,
187
+ data: mockData.getAllTemplatesListSuccess,
188
+ weCRMTemplate: mockData.getAllTemplatesListSuccess,
195
189
  isReset: mockData.getAllTemplatesListSuccess?.queryParams?.page === 1,
196
190
  })
197
191
  .run();
@@ -210,163 +204,6 @@ describe('templateList saga', () => {
210
204
  })
211
205
  .run();
212
206
  });
213
-
214
- it('SMS channel uses fetchSmsTemplatesFromQuery', () => {
215
- const smsAction = {
216
- type: types.GET_ALL_TEMPLATES_REQUEST,
217
- channel: 'Sms',
218
- queryParams: { page: 1, perPage: 25, name: '', sortBy: 'Most Recent' },
219
- intlCopyOf: 'Kopie',
220
- };
221
- const fetched = {
222
- channelTemplates: { templates: [{ name: 'Kopie Foo' }], totalCount: 1 },
223
- weCRMTemplate: undefined,
224
- raw: {},
225
- };
226
- return expectSaga(getAllTemplates, smsAction)
227
- .provide([
228
- [call(fetchSmsTemplatesFromQuery, smsAction.queryParams, smsAction.intlCopyOf), fetched],
229
- ])
230
- .put({
231
- type: types.GET_ALL_TEMPLATES_SUCCESS,
232
- data: fetched.channelTemplates,
233
- weCRMTemplate: fetched.weCRMTemplate,
234
- isReset: true,
235
- })
236
- .run();
237
- });
238
-
239
- it('SMS channel propagates fetchSmsTemplatesFromQuery errors', () => {
240
- const smsAction = {
241
- type: types.GET_ALL_TEMPLATES_REQUEST,
242
- channel: 'SMS',
243
- queryParams: { page: 2, perPage: 25 },
244
- intlCopyOf: '',
245
- };
246
- const error = new Error('sms list fail');
247
- return expectSaga(getAllTemplates, smsAction)
248
- .provide([
249
- [call(fetchSmsTemplatesFromQuery, smsAction.queryParams, smsAction.intlCopyOf), throwError(error)],
250
- ])
251
- .put({
252
- type: types.GET_ALL_TEMPLATES_FAILURE,
253
- error,
254
- })
255
- .run();
256
- });
257
-
258
- it('wechat channel merges mapped and richmedia and applies Most Recent sort', () => {
259
- const wechatApiResponse = {
260
- response: {
261
- mapped: [
262
- { name: 'B', updatedAt: '2024-01-02T00:00:00Z' },
263
- { name: 'A', updatedAt: '2024-01-01T00:00:00Z' },
264
- ],
265
- richmedia: [{ name: 'C', updatedAt: '2024-01-03T00:00:00Z' }],
266
- unMapped: [],
267
- },
268
- };
269
- const action = {
270
- channel: 'wechat',
271
- queryParams: { sortBy: 'Most Recent', page: 1 },
272
- intlCopyOf: '',
273
- };
274
- return expectSaga(getAllTemplates, action)
275
- .provide([[matchers.call.fn(api.getAllTemplates), wechatApiResponse]])
276
- .put.actionType(types.GET_ALL_TEMPLATES_SUCCESS)
277
- .run();
278
- });
279
-
280
- it('wechat channel applies Alphabetically sort', () => {
281
- const wechatApiResponse = {
282
- response: {
283
- mapped: [{ name: 'Zebra', updatedAt: '2024-01-02T00:00:00Z' }],
284
- richmedia: [{ name: 'Apple', updatedAt: '2024-01-01T00:00:00Z' }],
285
- unMapped: [],
286
- },
287
- };
288
- const action = {
289
- channel: 'wechat',
290
- queryParams: { sortBy: 'Alphabetically', page: 1 },
291
- intlCopyOf: '',
292
- };
293
- return expectSaga(getAllTemplates, action)
294
- .provide([[matchers.call.fn(api.getAllTemplates), wechatApiResponse]])
295
- .put.actionType(types.GET_ALL_TEMPLATES_SUCCESS)
296
- .run();
297
- });
298
-
299
- it('wechat channel replaces Copy-of prefix when intlCopyOf is set', () => {
300
- const wechatApiResponse = {
301
- response: {
302
- mapped: [{ name: 'Copy of Promo', updatedAt: '2024-01-01T00:00:00Z' }],
303
- richmedia: [],
304
- unMapped: [],
305
- },
306
- };
307
- const action = {
308
- channel: 'wechat',
309
- queryParams: { page: 1 },
310
- intlCopyOf: 'Kopia av ',
311
- };
312
- return expectSaga(getAllTemplates, action)
313
- .provide([[matchers.call.fn(api.getAllTemplates), wechatApiResponse]])
314
- .put.actionType(types.GET_ALL_TEMPLATES_SUCCESS)
315
- .run();
316
- });
317
- });
318
-
319
- describe('getLocalSmsTemplates saga', () => {
320
- it('invokes onSuccess with fetched data', () => {
321
- const onSuccess = jest.fn();
322
- const action = {
323
- queryParams: { page: 1 },
324
- intlCopyOf: '',
325
- onSuccess,
326
- };
327
- const fetched = { channelTemplates: { templates: [] }, raw: {} };
328
- return expectSaga(getLocalSmsTemplates, action)
329
- .provide([[call(fetchSmsTemplatesFromQuery, action.queryParams, action.intlCopyOf), fetched]])
330
- .call(onSuccess, fetched)
331
- .run();
332
- });
333
-
334
- it('invokes onFailure when fetch throws', () => {
335
- const onFailure = jest.fn();
336
- const error = new Error('local list fail');
337
- const action = {
338
- queryParams: { page: 1 },
339
- intlCopyOf: 'Kopie',
340
- onFailure,
341
- };
342
- return expectSaga(getLocalSmsTemplates, action)
343
- .provide([
344
- [call(fetchSmsTemplatesFromQuery, action.queryParams, action.intlCopyOf), throwError(error)],
345
- ])
346
- .call(onFailure, error)
347
- .run();
348
- });
349
-
350
- it('does not throw when callbacks are omitted', () => {
351
- const action = { queryParams: { page: 1 }, intlCopyOf: '' };
352
- return expectSaga(getLocalSmsTemplates, action)
353
- .provide([
354
- [call(fetchSmsTemplatesFromQuery, action.queryParams, action.intlCopyOf), throwError(new Error('x'))],
355
- ])
356
- .run();
357
- });
358
-
359
- it('does not call onSuccess when it is not a function', () => {
360
- const action = {
361
- queryParams: { page: 1 },
362
- intlCopyOf: '',
363
- onSuccess: 'not-a-fn',
364
- };
365
- const fetched = { channelTemplates: { templates: [] } };
366
- return expectSaga(getLocalSmsTemplates, action)
367
- .provide([[call(fetchSmsTemplatesFromQuery, action.queryParams, action.intlCopyOf), fetched]])
368
- .run();
369
- });
370
207
  });
371
208
 
372
209
  describe('watchForTemplates saga', () => {
@@ -560,7 +397,7 @@ describe('getSenderDetails Saga', () => {
560
397
 
561
398
  describe('fetchWeCrmAccounts Saga', () => {
562
399
  const action = { source: 'CRM' };
563
- it('handles successful fetching of WeCRM accounts', () => {
400
+ test.concurrent('handles successful fetching of WeCRM accounts', () => {
564
401
  const fakeResponse = {
565
402
  response: [{ id: 1, name: 'Account One' }]
566
403
  };
@@ -573,11 +410,14 @@ describe('fetchWeCrmAccounts Saga', () => {
573
410
  })
574
411
  );
575
412
  });
576
- it('handles error thrown from api', () => {
413
+ test.concurrent('handles error thrown from api', () => {
577
414
  const error = new Error('Fetch failed');
578
- expectSaga(fetchWeCrmAccounts, action)
415
+ expectSaga(fetchWeCrmAccounts, action.source)
579
416
  .provide([
580
- [call(api.fetchWeCrmAccounts, action.source), throwError(error)],
417
+ [
418
+ call(api.fetchWeCrmAccounts),
419
+ throwError(error)
420
+ ],
581
421
  ])
582
422
  .put({
583
423
  type: types.GET_WECRM_ACCOUNTS_FAILURE,
@@ -619,29 +459,6 @@ describe('sendZippedFile Saga', () => {
619
459
  expect(generator.next().value).toEqual(call(api.sendZippedFile, action.selectedFile));
620
460
  });
621
461
 
622
- it('calls errorHandler with message and returns early when isError is true', () => {
623
- const errorHandler = jest.fn();
624
- const successHandler = jest.fn();
625
- const errorAction = {
626
- selectedFile: 'upload.zip',
627
- errorHandler,
628
- successHandler,
629
- };
630
- const fakeErrorResponse = {
631
- status: { isError: true },
632
- message: 'Upload failed',
633
- };
634
- const generator = sendZippedFile(errorAction);
635
- expect(generator.next().value).toEqual(call(api.sendZippedFile, errorAction.selectedFile));
636
- // Advancing with isError response causes the saga to call errorHandler and yield
637
- const yieldStep = generator.next(fakeErrorResponse);
638
- expect(errorHandler).toHaveBeenCalledWith('Upload failed');
639
- expect(yieldStep.done).toBe(false);
640
- // After resuming from the yield, the return statement completes the generator
641
- expect(generator.next().done).toBe(true);
642
- expect(successHandler).not.toHaveBeenCalled();
643
- });
644
-
645
462
  it('handles error thrown from api', () => {
646
463
  const error = new Error('Fetch failed');
647
464
  expectSaga(sendZippedFile, action)
@@ -1407,7 +1224,7 @@ describe('v2TemplateSaga', () => {
1407
1224
  // all() returns an IO object with an ALL key containing the array of effects
1408
1225
  expect(step.value).toHaveProperty('@@redux-saga/IO', true);
1409
1226
  expect(step.value).toHaveProperty('ALL');
1410
- expect(step.value.ALL).toHaveLength(15);
1227
+ expect(step.value.ALL).toHaveLength(14);
1411
1228
  expect(step.done).toBe(false);
1412
1229
  });
1413
1230
  });
@@ -10,10 +10,10 @@ export default css`
10
10
 
11
11
  .component-wrapper {
12
12
  ${(props) => props.isFullMode ? `
13
- max-width: 71.25rem;
14
- margin: 0 auto;
13
+ max-width: 100%;
14
+ margin: 0;
15
15
  width: 100%;
16
- padding: 0.714rem 0;
16
+ padding: 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 {
@@ -23,80 +23,15 @@ 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`
100
- ${(props) => props.isFullMode ? `margin-top: ${CAP_SPACE_24}` : ``
29
+ ${(props) => props.isFullMode ? `margin-top: ${CAP_SPACE_24};` : ``
30
+ }
31
+ .ant-tabs-nav-list {
32
+ gap: 2rem;
33
+ }
34
+ .ant-tabs-tab + .ant-tabs-tab {
35
+ margin: 0 !important;
101
36
  }
102
37
  `;
@@ -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';
@@ -41,19 +41,22 @@ import { makeSelectAuthenticated, selectCurrentOrgDetails } from "../../v2Contai
41
41
  import {
42
42
  CALL_TASK,
43
43
  COMMON_CHANNELS,
44
- LOCAL_TEMPLATE_CONFIG_KEYS_FOR_PICK,
45
44
  LOYALTY_SUPPORTED_ACTION,
46
45
  MOBILE_PUSH,
47
46
  NORMALIZED_CHANNEL_ALIASES,
48
47
  SMS,
49
48
  } from "../CreativesContainer/constants";
49
+ import { MFEEventBus } from '@capillarytech/cap-ui-utils';
50
+ import { isMFEMode } from '../../utils/mfeDetect';
51
+ import appConfig from '../../../app-config';
50
52
 
51
- const { CapCustomCardList } = CapCustomCard;
53
+ const {CapCustomCardList} = CapCustomCard;
52
54
 
53
55
  const StyledCapTab = withStyles(CapTab, CapTabStyle);
54
56
  export class TemplatesV2 extends React.Component { // eslint-disable-line react/prefer-stateless-function
55
57
  constructor(props) {
56
58
  super(props);
59
+ this.lcpReported = false;
57
60
  let defaultChannel = get(this, 'props.channel') || get(this, 'props.params.channel') || 'sms';
58
61
  if (defaultChannel === LOYALTY) {
59
62
  defaultChannel = 'sms';
@@ -125,9 +128,9 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
125
128
  return !normalizedChannelsToHideSet.has(paneKey);
126
129
  });
127
130
 
128
- if (isFullMode && !normalizedChannelsToHideSet.has(normalizeChannel(ASSETS))) {
129
- filteredPanes.push({ content: <div></div>, tab: intl.formatMessage(messages.gallery), key: ASSETS });
130
- } else if (!isFullMode) {
131
+ if (isFullMode) {
132
+ filteredPanes.push({ content: <div></div>, tab: intl.formatMessage(messages.gallery), key: 'assets' });
133
+ } else {
131
134
  // Add special-mode panes only when not hidden (use normalized checks)
132
135
  if (!normalizedChannelsToHideSet.has(CALL_TASK.toLowerCase())) {
133
136
  filteredPanes.push({ content: <div></div>, tab: intl.formatMessage(messages.callTask), key: CALL_TASK.toLowerCase() });
@@ -222,14 +225,30 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
222
225
  }
223
226
  }
224
227
 
228
+ componentDidUpdate(prevProps) {
229
+ if (
230
+ isMFEMode() &&
231
+ !this.lcpReported &&
232
+ !this.props.Templates.getAllTemplatesInProgress &&
233
+ prevProps.Templates.getAllTemplatesInProgress
234
+ ) {
235
+ this.lcpReported = true;
236
+ MFEEventBus.emit('mfe:segment', {
237
+ action: 'stop',
238
+ appId: appConfig.appName,
239
+ type: 'lcp',
240
+ timestamp: performance.now(),
241
+ });
242
+ }
243
+ }
244
+
225
245
  componentWillReceiveProps(nextProps) {
226
246
  if (this.props.channel !== nextProps.channel) {
227
247
  const panes = this.setChannelContent(nextProps.channel, this.state.panes);
228
248
  this.setState({selectedChannel: nextProps.channel, panes });
229
249
  }
230
250
  }
231
-
232
- getTemplateDataForGrid = ({ templates, handlers, filterContent, channel, isLoading, loadingTip }) => {
251
+ getTemplateDataForGrid = ({templates, handlers, filterContent, channel, isLoading, loadingTip}) => {
233
252
  const currentChannel = channel.toUpperCase();
234
253
  const cardDataList = templates.map((template) => {
235
254
  const templateData =
@@ -255,8 +274,7 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
255
274
  </CapSpin>
256
275
 
257
276
  </div>);
258
- };
259
-
277
+ }
260
278
  getGalleryComponent = (location) => <Gallery location={location} isFullMode={this.props.isFullMode}/>
261
279
  getCallTaskComponent = () => (
262
280
  <CallTask
@@ -320,29 +338,6 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
320
338
  if (messageStrategy !== "X_ENGAGE" && channel === 'facebook' && !isFullMode) {
321
339
  return this.getFacebookComponent();
322
340
  }
323
- const localConfig = this.props.localTemplatesConfig || pick(this.props, LOCAL_TEMPLATE_CONFIG_KEYS_FOR_PICK);
324
- const useLocalTemplates = localConfig.useLocalTemplates;
325
- if (useLocalTemplates && channel === (this.props.channel || 'sms')) {
326
- // Reuse full Templates component (same UI as Redux flow) with local data only
327
- const location = { pathname: `/${channel}`, search: '', query: !this.props.isFullMode ? { type: 'embedded', module: 'library' } : {} };
328
- return (
329
- <Templates
330
- key={`${channel}-local`}
331
- location={location}
332
- route={{ name: channel }}
333
- router={this.props.router}
334
- isFullMode={this.props.isFullMode}
335
- createNew={this.props.createNew}
336
- onSelectTemplate={this.props.onSelectTemplate}
337
- handlePeviewTemplate={this.props.handlePeviewTemplate}
338
- messageStrategy={this.props.messageStrategy}
339
- smsRegister={this.props.smsRegister}
340
- hideTestAndPreviewBtn={this.props.hideTestAndPreviewBtn}
341
- localTemplatesConfig={localConfig}
342
- />
343
- );
344
- }
345
-
346
341
  const location = {pathname: `/${channel}`, search: '', query};
347
342
  switch (channel) {
348
343
  case 'call_task':
@@ -392,55 +387,28 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
392
387
  }
393
388
  render() {
394
389
  const { isFullMode, className, cap = {}, Global = {}} = this.props;
395
- const useLocalTemplates = get(this.props, 'localTemplatesConfig.useLocalTemplates', false);
396
390
  const { accessiblePermissions = []} = cap.user || Global.user || {};
397
391
  let isCreativeAccessible = true;
398
392
  if (!accessiblePermissions.includes(CREATIVES_UI_VIEW)) {
399
393
  isCreativeAccessible = false;
400
394
  }
401
- // Recompute active pane content every render so local-list mode updates
402
- // (templates/loading/search UI) are not stuck with the initial cached pane.
403
- const panes = this.setChannelContent(this.state.selectedChannel, this.state.panes);
404
- const hideChannelTabsForLocalSms = useLocalTemplates && panes.length === 1;
405
- const activeLocalPane = hideChannelTabsForLocalSms
406
- ? (panes.find(
407
- (p) => String(p.key).toLowerCase() === String(this.state.selectedChannel).toLowerCase(),
408
- ) || panes[0])
409
- : null;
410
395
  return (
411
396
  !isCreativeAccessible ? <AccessForbidden /> : (
412
- <div
413
- className={`${className} creatives-templates-container ${isFullMode ? 'fullmode' : 'library-mode'}${useLocalTemplates ? ' creatives-templates-container--local-sms' : ''}`}
414
- data-testid="cap-wrapper"
415
- >
416
- {isFullMode && !useLocalTemplates && (
417
- <Helmet
418
- title={this.props.intl.formatMessage(messages.creatives)}
419
- meta={[
420
- { name: 'description', content: this.props.intl.formatMessage(messages.creativesDesc) },
421
- ]}
422
- />
423
- )}
397
+ <div className={`${className} creatives-templates-container ${isFullMode ? 'fullmode' : 'library-mode'}`} data-testid="cap-wrapper">
398
+ {isFullMode && <Helmet
399
+ title={this.props.intl.formatMessage(messages.creatives)}
400
+ meta={[
401
+ { name: 'description', content: this.props.intl.formatMessage(messages.creativesDesc) },
402
+ ]}
403
+ />}
424
404
  <div className="component-wrapper">
425
- {isFullMode && (
426
- <CapHeader
427
- title={<FormattedMessage {...messages.creatives} />}
428
- {...(!useLocalTemplates && {
429
- description: <FormattedMessage {...messages.creativesDesc} />,
430
- })}
431
- />
432
- )}
433
- {hideChannelTabsForLocalSms ? (
434
- <div className="templates-v2-local-sms-pane">{activeLocalPane?.content}</div>
435
- ) : (
436
- <StyledCapTab
437
- panes={panes}
438
- onChange={this.channelChange}
439
- activeKey={this.state.selectedChannel}
440
- defaultActiveKey={this.state.selectedChannel}
441
- isFullMode={isFullMode}
442
- />
443
- )}
405
+ <StyledCapTab
406
+ panes={this.state.panes}
407
+ onChange={this.channelChange}
408
+ activeKey={this.state.selectedChannel}
409
+ defaultActiveKey={this.state.selectedChannel}
410
+ isFullMode={isFullMode}
411
+ />
444
412
  </div>
445
413
  </div>
446
414
  )
@@ -472,17 +440,6 @@ TemplatesV2.propTypes = {
472
440
  currentOrgDetails: PropTypes.object,
473
441
  restrictPersonalization: PropTypes.bool,
474
442
  isAnonymousType: PropTypes.bool,
475
- // Optional: reuse grid UI with local template list (e.g. SMS fallback). Pass object or same keys as individual props.
476
- localTemplatesConfig: PropTypes.shape({
477
- useLocalTemplates: PropTypes.bool,
478
- localTemplates: PropTypes.arrayOf(PropTypes.object),
479
- localTemplatesLoading: PropTypes.bool,
480
- localTemplatesLoadingTip: PropTypes.string,
481
- localTemplatesFilterContent: PropTypes.node,
482
- localTemplatesFooterContent: PropTypes.node,
483
- localTemplatesOnPageChange: PropTypes.func,
484
- localTemplatesUseSkeleton: PropTypes.bool,
485
- }),
486
443
  };
487
444
 
488
445
  TemplatesV2.defaultProps = {
@@ -11,7 +11,7 @@ describe('WeChat Template Management Sagas', () => {
11
11
  describe('getDefaultWeChatTemplates Saga', () => {
12
12
  const params = { channel: 'WeChat', queryParams: { type: 'default' } };
13
13
 
14
- it('handles fetching default WeChat templates successfully', () => {
14
+ it.concurrent('handles fetching default WeChat templates successfully', () => {
15
15
  const fakeResponse = {
16
16
  response: {
17
17
  unMapped: [{ id: 1, name: 'Template One' }],
@@ -31,7 +31,7 @@ describe('WeChat Template Management Sagas', () => {
31
31
  .run();
32
32
  });
33
33
 
34
- it('handles failure in fetching default WeChat templates', () => {
34
+ it.concurrent('handles failure in fetching default WeChat templates', () => {
35
35
  const error = new Error('Fetch failed');
36
36
 
37
37
  return expectSaga(sagas.getDefaultWeChatTemplates, params)
@@ -48,7 +48,7 @@ describe('WeChat Template Management Sagas', () => {
48
48
 
49
49
  describe('createTemplate Saga', () => {
50
50
  const template = { name: 'New WeChat Template' };
51
- test('handles creating a WeChat template successfully', () => {
51
+ test.concurrent('handles creating a WeChat template successfully', () => {
52
52
  const fakeResponse = {
53
53
  response: template,
54
54
  status: { code: 200 }
@@ -64,7 +64,7 @@ describe('WeChat Template Management Sagas', () => {
64
64
  })
65
65
  .run();
66
66
  });
67
- test('handles failure when creating a WeChat template', () => {
67
+ test.concurrent('handles failure when creating a WeChat template', () => {
68
68
  const error = new Error('Creation failed');
69
69
  return expectSaga(sagas.createTemplate, template)
70
70
  .provide([
@@ -79,7 +79,7 @@ describe('WeChat Template Management Sagas', () => {
79
79
  });
80
80
 
81
81
  describe('fetchWeCrmAccounts Saga', () => {
82
- it('handles fetching WeCrm accounts successfully', () => {
82
+ it.concurrent('handles fetching WeCrm accounts successfully', () => {
83
83
  const fakeResponse = {
84
84
  response: [{ id: 1, name: 'Account One' }]
85
85
  };
@@ -95,7 +95,7 @@ describe('WeChat Template Management Sagas', () => {
95
95
  .run();
96
96
  });
97
97
 
98
- it('handles failure in fetching WeCrm accounts', () => {
98
+ it.concurrent('handles failure in fetching WeCrm accounts', () => {
99
99
  const error = new Error('Fetch failed');
100
100
 
101
101
  return expectSaga(sagas.fetchWeCrmAccounts)
@@ -113,7 +113,7 @@ describe('WeChat Template Management Sagas', () => {
113
113
  describe('getTemplateDetails Saga', () => {
114
114
  const id = '123';
115
115
 
116
- it('handles fetching template details successfully', () => {
116
+ it.concurrent('handles fetching template details successfully', () => {
117
117
  const fakeResponse = {
118
118
  response: { id: 123, name: 'Detailed Template' }
119
119
  };
@@ -129,7 +129,7 @@ describe('WeChat Template Management Sagas', () => {
129
129
  .run();
130
130
  });
131
131
 
132
- it('handles failure in fetching template details', () => {
132
+ it.concurrent('handles failure in fetching template details', () => {
133
133
  const error = new Error('Fetch failed');
134
134
 
135
135
  return expectSaga(sagas.getTemplateDetails, id)
@@ -148,7 +148,7 @@ describe('WeChat Template Management Sagas', () => {
148
148
 
149
149
  // Test combined saga
150
150
  describe('Combined v2WechatMapTemplatesSagas', () => {
151
- it('should initialize all WeChat-related watcher sagas without error', () => {
151
+ it.concurrent('should initialize all WeChat-related watcher sagas without error', () => {
152
152
  return expectSaga(v2WechatMapTemplatesSagas)
153
153
  .run();
154
154
  });