@capillarytech/creatives-library 8.0.359-alpha.0 → 8.0.359-alpha.1

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 (147) hide show
  1. package/constants/unified.js +29 -0
  2. package/index.html +1 -0
  3. package/package.json +1 -1
  4. package/services/tests/api.test.js +35 -20
  5. package/utils/cdnTransformation.js +75 -3
  6. package/utils/commonUtils.js +19 -1
  7. package/utils/rcsPayloadUtils.js +92 -0
  8. package/utils/templateVarUtils.js +201 -0
  9. package/utils/tests/cdnTransformation.test.js +127 -0
  10. package/utils/tests/rcsPayloadUtils.test.js +226 -0
  11. package/utils/tests/templateVarUtils.test.js +204 -0
  12. package/v2Components/CapActionButton/constants.js +7 -0
  13. package/v2Components/CapActionButton/index.js +166 -108
  14. package/v2Components/CapActionButton/index.scss +157 -6
  15. package/v2Components/CapActionButton/messages.js +19 -3
  16. package/v2Components/CapActionButton/tests/index.test.js +41 -17
  17. package/v2Components/CapImageUpload/index.js +2 -2
  18. package/v2Components/CapTagList/index.js +10 -0
  19. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +72 -49
  20. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
  21. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +214 -21
  22. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
  23. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +83 -9
  24. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
  25. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +79 -11
  26. package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
  27. package/v2Components/CommonTestAndPreview/UnifiedPreview/PreviewHeader.js +16 -0
  28. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +157 -15
  29. package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberPreviewContent.js +14 -132
  30. package/v2Components/CommonTestAndPreview/UnifiedPreview/WebPushPreviewContent.js +169 -0
  31. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +400 -239
  32. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +202 -10
  33. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
  34. package/v2Components/CommonTestAndPreview/constants.js +40 -2
  35. package/v2Components/CommonTestAndPreview/index.js +887 -453
  36. package/v2Components/CommonTestAndPreview/messages.js +45 -3
  37. package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
  38. package/v2Components/CommonTestAndPreview/sagas.js +25 -6
  39. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +308 -284
  40. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +231 -65
  41. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
  42. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
  43. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
  44. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +34 -13
  45. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/PreviewHeader.test.js +163 -0
  46. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
  47. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/ViberPreviewContent.test.js +0 -364
  48. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/WebPushPreviewContent.test.js +522 -0
  49. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +454 -1
  50. package/v2Components/CommonTestAndPreview/tests/constants.test.js +2 -1
  51. package/v2Components/CommonTestAndPreview/tests/index.test.js +327 -4
  52. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
  53. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +31 -24
  54. package/v2Components/FormBuilder/index.js +167 -56
  55. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +91 -0
  56. package/v2Components/SmsFallback/constants.js +73 -0
  57. package/v2Components/SmsFallback/index.js +956 -0
  58. package/v2Components/SmsFallback/index.scss +265 -0
  59. package/v2Components/SmsFallback/messages.js +78 -0
  60. package/v2Components/SmsFallback/smsFallbackUtils.js +119 -0
  61. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
  62. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
  63. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
  64. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +223 -0
  65. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +309 -0
  66. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
  67. package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
  68. package/v2Components/TemplatePreview/_templatePreview.scss +37 -22
  69. package/v2Components/TemplatePreview/constants.js +2 -0
  70. package/v2Components/TemplatePreview/index.js +143 -31
  71. package/v2Components/TemplatePreview/tests/index.test.js +142 -0
  72. package/v2Components/TestAndPreviewSlidebox/index.js +15 -3
  73. package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
  74. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
  75. package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
  76. package/v2Components/VarSegmentMessageEditor/index.js +125 -0
  77. package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
  78. package/v2Containers/App/constants.js +3 -0
  79. package/v2Containers/App/tests/constants.test.js +61 -0
  80. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +17 -0
  81. package/v2Containers/CreativesContainer/SlideBoxContent.js +36 -4
  82. package/v2Containers/CreativesContainer/SlideBoxFooter.js +14 -5
  83. package/v2Containers/CreativesContainer/SlideBoxHeader.js +36 -5
  84. package/v2Containers/CreativesContainer/constants.js +9 -0
  85. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +79 -0
  86. package/v2Containers/CreativesContainer/index.js +382 -127
  87. package/v2Containers/CreativesContainer/index.scss +83 -1
  88. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
  89. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +79 -34
  90. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +79 -16
  91. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
  92. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +357 -98
  93. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
  94. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
  95. package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
  96. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
  97. package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
  98. package/v2Containers/Rcs/constants.js +120 -11
  99. package/v2Containers/Rcs/index.js +2577 -812
  100. package/v2Containers/Rcs/index.scss +281 -8
  101. package/v2Containers/Rcs/messages.js +34 -3
  102. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +225 -0
  103. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +98036 -70145
  104. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
  105. package/v2Containers/Rcs/tests/index.test.js +152 -121
  106. package/v2Containers/Rcs/tests/mockData.js +38 -0
  107. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +318 -0
  108. package/v2Containers/Rcs/tests/utils.test.js +646 -30
  109. package/v2Containers/Rcs/utils.js +478 -11
  110. package/v2Containers/Sms/Create/index.js +106 -40
  111. package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
  112. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  113. package/v2Containers/SmsTrai/Create/index.js +9 -4
  114. package/v2Containers/SmsTrai/Edit/constants.js +2 -0
  115. package/v2Containers/SmsTrai/Edit/index.js +640 -130
  116. package/v2Containers/SmsTrai/Edit/index.scss +121 -0
  117. package/v2Containers/SmsTrai/Edit/messages.js +14 -4
  118. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4328 -2375
  119. package/v2Containers/SmsWrapper/index.js +37 -8
  120. package/v2Containers/TagList/index.js +6 -0
  121. package/v2Containers/Templates/TemplatesActionBar.js +101 -0
  122. package/v2Containers/Templates/_templates.scss +166 -86
  123. package/v2Containers/Templates/actions.js +11 -0
  124. package/v2Containers/Templates/constants.js +2 -0
  125. package/v2Containers/Templates/index.js +203 -145
  126. package/v2Containers/Templates/sagas.js +62 -13
  127. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
  128. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1062 -1017
  129. package/v2Containers/Templates/tests/sagas.test.js +222 -22
  130. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
  131. package/v2Containers/Templates/tests/webpush.test.js +375 -0
  132. package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
  133. package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
  134. package/v2Containers/TemplatesV2/index.js +86 -23
  135. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
  136. package/v2Containers/Viber/constants.js +0 -19
  137. package/v2Containers/Viber/index.js +47 -714
  138. package/v2Containers/Viber/index.scss +0 -148
  139. package/v2Containers/Viber/messages.js +0 -116
  140. package/v2Containers/Viber/tests/index.test.js +0 -80
  141. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
  142. package/v2Containers/WebPush/Create/index.js +91 -8
  143. package/v2Containers/WebPush/Create/index.scss +7 -0
  144. package/v2Containers/WebPush/Create/tests/getTemplateContent.test.js +348 -0
  145. package/v2Containers/WebPush/Create/tests/testAndPreviewIntegration.test.js +325 -0
  146. package/v2Containers/Whatsapp/index.js +3 -20
  147. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
@@ -72,7 +72,6 @@ import * as ebillActions from '../Ebill/actions';
72
72
  import * as emailActions from '../Email/actions';
73
73
  import * as lineActions from '../Line/Container/actions';
74
74
  import * as viberActions from '../Viber/actions';
75
- import { VIBER_MEDIA_TYPES } from '../Viber/constants';
76
75
  import * as facebookActions from '../Facebook/actions';
77
76
  import * as whatsappActions from '../Whatsapp/actions';
78
77
  import * as rcsActions from '../Rcs/actions';
@@ -105,6 +104,9 @@ import {
105
104
  VIBER as VIBER_CHANNEL,
106
105
  FACEBOOK as FACEBOOK_CHANNEL,
107
106
  CREATE,
107
+ EXTERNAL_URL,
108
+ URL,
109
+ SITE_URL,
108
110
  } from '../App/constants';
109
111
  import {MAX_WHATSAPP_TEMPLATES, WARNING_WHATSAPP_TEMPLATES , ACCOUNT_MAPPING_ON_CHANNEL, noFilteredWhatsappZaloTemplatesTitle, noFilteredWhatsappZaloTemplatesDesc, noApprovedWhatsappZaloTemplatesTitle, noApprovedWhatsappTemplatesDesc, zaloDescIllustration, noApprovedRcsTemplatesTitle, noApprovedRcsTemplatesDesc, ARCHIVE_STATUS_ACTIVE, ARCHIVE_STATUS_ARCHIVED, ARCHIVE_REFRESH_TYPE_ARCHIVE, ARCHIVE_REFRESH_TYPE_UNARCHIVE} from './constants';
110
112
  import { COPY_OF, EMBEDDED } from '../../constants/unified';
@@ -123,7 +125,7 @@ import { INAPP_LAYOUT_DETAILS, INAPP_MESSAGE_LAYOUT_TYPES } from '../InApp/const
123
125
  import { ZALO_STATUS_OPTIONS, ZALO_STATUSES } from '../Zalo/constants';
124
126
  import { getWhatsappContent, getWhatsappStatus, getWhatsappCategory, getWhatsappCta, getWhatsappQuickReply, getWhatsappAutoFill, getWhatsappCarouselButtonView } from '../Whatsapp/utils';
125
127
  import { getRCSContent } from '../Rcs/utils';
126
- import {RCS_STATUSES} from '../Rcs/constants';
128
+ import { RCS_STATUSES, HOST_INFOBIP } from '../Rcs/constants';
127
129
  import zaloMessages from '../Zalo/messages';
128
130
  import rcsMessages from '../Rcs/messages';
129
131
  import inAppMessages from '../InApp/messages';
@@ -461,7 +463,13 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
461
463
  if (this.props.location.query.type === 'embedded') {
462
464
  this.props.actions.resetAccount();
463
465
  }
464
- if (['line', VIBER_CHANNEL, FACEBOOK_CHANNEL, 'sms', 'email', 'ebill'].includes((this.state.channel || '').toLowerCase())) {
466
+ // When using local templates (e.g. SMS fallback selector), do not fetch from API or we overwrite global store and break background RCS list
467
+ const useLocalTemplates = get(
468
+ this.props,
469
+ 'localTemplatesConfig.useLocalTemplates',
470
+ get(this.props, 'useLocalTemplates', false),
471
+ );
472
+ if (!useLocalTemplates && [LINE_LOWERCASE, VIBER_CHANNEL, FACEBOOK_CHANNEL, SMS_LOWERCASE, EMAIL_LOWERCASE, EBILL_LOWERCASE].includes((this.state.channel || '').toLowerCase())) {
465
473
  const queryParams = {
466
474
  // name: this.state.searchText,
467
475
  // sortBy: this.state.sortBy,
@@ -544,7 +552,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
544
552
  selectedTemplateForPreview: null,
545
553
  testAndPreviewContent: null,
546
554
  }, () => {
547
- if (['line', VIBER_CHANNEL, FACEBOOK_CHANNEL, 'sms', 'email', 'ebill', RCS_LOWERCASE].includes(this.state.channel.toLowerCase()) || (this.state.channel.toLowerCase() === 'wechat' && !isEmpty(nextProps.Templates.selectedWeChatAccount))) {
555
+ if ([LINE_LOWERCASE, VIBER_CHANNEL, FACEBOOK_CHANNEL, SMS_LOWERCASE, EMAIL_LOWERCASE, EBILL_LOWERCASE, RCS_LOWERCASE].includes(this.state.channel.toLowerCase()) || (this.state.channel.toLowerCase() === 'wechat' && !isEmpty(nextProps.Templates.selectedWeChatAccount))) {
548
556
  if (this.state.channel.toLowerCase() === 'wechat' && !isEmpty(nextProps.Templates.selectedWeChatAccount)) {
549
557
  params.wecrmId = (nextProps.Templates.selectedWeChatAccount.configs || {}).wecrm_app_id;
550
558
  params.wecrmToken = (nextProps.Templates.selectedWeChatAccount.configs || {}).wecrm_token;
@@ -1007,8 +1015,16 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1007
1015
 
1008
1016
  componentWillUnmount() {
1009
1017
  window.removeEventListener("message", this.handleFrameTasks);
1010
- this.props.actions.resetTemplateStoreData();
1011
- this.props.globalActions.clearMetaEntities();
1018
+ // When using local templates (e.g. SMS fallback selector), do not clear global store or background RCS list is wiped
1019
+ const useLocalTemplates = get(
1020
+ this.props,
1021
+ 'localTemplatesConfig.useLocalTemplates',
1022
+ get(this.props, 'useLocalTemplates', false),
1023
+ );
1024
+ if (!useLocalTemplates) {
1025
+ this.props.actions.resetTemplateStoreData();
1026
+ this.props.globalActions.clearMetaEntities();
1027
+ }
1012
1028
  // Clear any pending timeouts to prevent memory leaks
1013
1029
  if (this._clearEditTimeout) {
1014
1030
  clearTimeout(this._clearEditTimeout);
@@ -1372,11 +1388,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1372
1388
  const image = viberContent.image || {};
1373
1389
  const video = viberContent.video || {};
1374
1390
  const button = viberContent.button || {};
1375
- const messageType = viberContent.type || '';
1376
- const cardsRaw = viberContent.cards;
1377
- const cards = Array.isArray(cardsRaw) ? cardsRaw : [];
1378
- const isCarousel =
1379
- messageType === VIBER_MEDIA_TYPES.CAROUSEL || cards.length > 0;
1380
1391
 
1381
1392
  const viberPreview = {
1382
1393
  messageContent: text,
@@ -1394,39 +1405,81 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1394
1405
  };
1395
1406
  }
1396
1407
 
1397
- if (isCarousel) {
1398
- viberPreview.type = VIBER_MEDIA_TYPES.CAROUSEL;
1399
- viberPreview.cards = cards.map((card) => ({
1400
- text: card?.text || '',
1401
- mediaUrl: card?.mediaUrl || '',
1402
- buttons: (card?.buttons || []).map((btn) => ({
1403
- title: btn?.title || '',
1404
- action: btn?.action || '',
1405
- })),
1406
- }));
1407
- viberPreview.showCarouselEditorPreview = true;
1408
- }
1409
-
1410
1408
  // Extract account name
1411
1409
  const accountName = get(template, 'definition.accountName', '');
1412
1410
 
1413
1411
  // Return Viber content object (same as Viber/index.js getTemplateContent)
1414
1412
  return {
1415
1413
  viberPreviewContent: viberPreview,
1416
- accountName: accountName || '',
1417
- brandName: accountName || '',
1418
- messageContent: text,
1419
- ...(isCarousel && {
1420
- type: VIBER_MEDIA_TYPES.CAROUSEL,
1421
- cards: viberPreview.cards,
1422
- }),
1423
- ...(!isCarousel && !isEmpty(button) && button.text && (button.url || viberContent.buttonURL) && {
1424
- button: {
1425
- text: button.text,
1426
- url: button.url || viberContent.buttonURL || '',
1427
- },
1428
- }),
1429
- buttonURL: button.url || viberContent.buttonURL || '',
1414
+ accountName: accountName ? [accountName] : [],
1415
+ brandName: accountName ? [accountName] : [],
1416
+ };
1417
+ }
1418
+
1419
+ case WEBPUSH: {
1420
+ // WebPush content is stored in creatives format (brandIcon, onClickAction, ctas)
1421
+ // Must be transformed to campaign/test-message format matching getTemplateContent() in WebPush/Create/index.js
1422
+ const webpushContent = get(baseContent, 'content.webpush', {});
1423
+ const {
1424
+ title = '',
1425
+ message = '',
1426
+ brandIcon,
1427
+ iconImageUrl: existingIconImageUrl,
1428
+ onClickAction,
1429
+ cta: existingCta,
1430
+ image,
1431
+ ctas: rawCtas,
1432
+ expandableDetails: existingExpandable,
1433
+ } = webpushContent;
1434
+ const accountId = get(template, 'definition.accountId', null);
1435
+ const templateName = template?.name || '';
1436
+
1437
+ // iconImageUrl stored as brandIcon in creatives format
1438
+ const iconImageUrl = brandIcon || existingIconImageUrl || undefined;
1439
+
1440
+ // cta stored as onClickAction in creatives format (type: URL|SITE_URL, url)
1441
+ // or already as cta in campaign format (type: EXTERNAL_URL|SITE_URL, actionLink)
1442
+ let cta = null;
1443
+ if (onClickAction) {
1444
+ const ctaType = onClickAction.type === URL ? EXTERNAL_URL : (onClickAction.type || SITE_URL);
1445
+ cta = { type: ctaType, actionLink: onClickAction.url || '' };
1446
+ } else if (existingCta) {
1447
+ cta = { type: existingCta.type || EXTERNAL_URL, actionLink: existingCta.actionLink || '' };
1448
+ }
1449
+
1450
+ // expandableDetails: image → media[], ctas[] → mapped ctas
1451
+ let expandableDetails = null;
1452
+ const hasImage = !!image;
1453
+ const hasCtas = Array.isArray(rawCtas) && rawCtas.length > 0;
1454
+ if (hasImage || hasCtas) {
1455
+ expandableDetails = {
1456
+ media: hasImage ? [{ url: image, type: IMAGE }] : [],
1457
+ ctas: hasCtas ? rawCtas.map((ctaItem) => ({
1458
+ type: ctaItem?.type === URL ? EXTERNAL_URL : (ctaItem?.type || EXTERNAL_URL),
1459
+ action: ctaItem?.action || '',
1460
+ title: ctaItem?.actionText || ctaItem?.title || '',
1461
+ actionLink: ctaItem?.actionLink || '',
1462
+ })) : [],
1463
+ };
1464
+ } else if (existingExpandable) {
1465
+ expandableDetails = {
1466
+ media: existingExpandable?.media || [],
1467
+ ctas: existingExpandable?.ctas || [],
1468
+ };
1469
+ }
1470
+
1471
+ return {
1472
+ channel: WEBPUSH,
1473
+ accountId,
1474
+ content: {
1475
+ title,
1476
+ message,
1477
+ ...(iconImageUrl ? { iconImageUrl } : {}),
1478
+ ...(cta ? { cta } : {}),
1479
+ ...(expandableDetails ? { expandableDetails } : {}),
1480
+ },
1481
+ messageSubject: templateName || title,
1482
+ offers: [],
1430
1483
  };
1431
1484
  }
1432
1485
 
@@ -1442,7 +1495,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1442
1495
  * @returns {Boolean} - True if channel supports Test and Preview
1443
1496
  */
1444
1497
  isTestAndPreviewSupported = () => {
1445
- const supportedChannels = [EMAIL, SMS, INAPP, MOBILE_PUSH, VIBER, ZALO];
1498
+ const supportedChannels = [EMAIL, SMS, INAPP, MOBILE_PUSH, VIBER, ZALO, WEBPUSH];
1446
1499
  return supportedChannels.includes(this.state.channel.toUpperCase());
1447
1500
  }
1448
1501
 
@@ -1828,12 +1881,20 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1828
1881
  }
1829
1882
 
1830
1883
  filterRcsTemplates = (templates) => {
1831
- let { selectedRcsStatus } = this.state;
1832
- selectedRcsStatus = !this.props.isFullMode ? RCS_STATUSES.approved : '';
1833
- if (selectedRcsStatus) {
1834
- return templates?.filter((template) => template?.versions?.base?.content?.RCS?.rcsContent?.cardContent?.[0]?.Status === selectedRcsStatus);
1884
+ const selectedRcsAccountName = this.props?.Templates?.selectedRcsAccount?.name || '';
1885
+ const hostName = this.state?.hostName;
1886
+ let nextTemplates = templates || [];
1887
+ if (selectedRcsAccountName) {
1888
+ nextTemplates = nextTemplates.filter(
1889
+ (t) => get(t, 'versions.base.content.RCS.rcsContent.accountName', '') === selectedRcsAccountName
1890
+ );
1835
1891
  }
1836
- return templates;
1892
+ if (!this.props.isFullMode && hostName !== HOST_INFOBIP) {
1893
+ return nextTemplates.filter(
1894
+ (t) => get(t, 'versions.base.content.RCS.rcsContent.cardContent[0].Status', 'unavailable') === RCS_STATUSES.approved
1895
+ );
1896
+ }
1897
+ return nextTemplates;
1837
1898
  }
1838
1899
 
1839
1900
  filterZaloTemplates = (templates) => {
@@ -2002,7 +2063,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2002
2063
  // Show preview icon only for channels that don't support Test and Preview
2003
2064
  (() => {
2004
2065
  // Channels that have Test and Preview integrated
2005
- const testAndPreviewChannels = [EMAIL, SMS, WHATSAPP, RCS, INAPP, MOBILE_PUSH, VIBER, ZALO];
2066
+ const testAndPreviewChannels = [EMAIL, SMS, WHATSAPP, RCS, INAPP, MOBILE_PUSH, VIBER, ZALO, WEBPUSH];
2006
2067
  const isTestAndPreviewSupported = testAndPreviewChannels.includes(currentChannel.toUpperCase());
2007
2068
 
2008
2069
  // Don't show preview icon if channel supports Test and Preview
@@ -2023,7 +2084,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2023
2084
  style={{ marginRight: "16px" }}
2024
2085
  type="eye"
2025
2086
  onClick={() => {
2026
- if (!this.props.isFullMode || this.props.isDltFromRcs) {
2087
+ if (!this.props.isFullMode || this.props.isDltFromRcs || this.props.isSmsFallbackFromRcs) {
2027
2088
  if (!get(template, "versions.base.content.zalo.previewUrl", "")) {
2028
2089
  this.setState({ zaloPreviewItemId: template?._id });
2029
2090
  }
@@ -2372,52 +2433,10 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2372
2433
  image = {},
2373
2434
  button = {},
2374
2435
  video = {},
2375
- type = '',
2376
- cards = [],
2377
2436
  } = {},
2378
2437
  } = {},
2379
2438
  } = template.versions.base;
2380
- const isViberCarousel = type === VIBER_MEDIA_TYPES.CAROUSEL;
2381
2439
  templateData.content = text;
2382
- if (isViberCarousel) {
2383
- const previewCards = Array.isArray(cards) ? cards.slice(0, 1) : [];
2384
- templateData.className = 'viber-carousel-static';
2385
- templateData.content = (
2386
- <CapRow className="viber-carousel-static-content">
2387
- <CapRow className="viber-carousel-static-message-box">
2388
- <CapLabel type="label1" className="viber-carousel-static-message">
2389
- {text}
2390
- </CapLabel>
2391
- </CapRow>
2392
- <CapRow className="viber-carousel-static-cards">
2393
- {previewCards.map((card, cardIdx) => (
2394
- <CapRow className="viber-carousel-static-card" key={`viber-static-card-${cardIdx}`}>
2395
- {card?.mediaUrl ? (
2396
- <CapImage src={card.mediaUrl} className="viber-carousel-static-image" />
2397
- ) : (
2398
- <CapRow className="viber-carousel-static-image-placeholder" />
2399
- )}
2400
- <CapLabel type="label1" className="viber-carousel-static-text">
2401
- {card?.text}
2402
- </CapLabel>
2403
- <CapRow className="viber-carousel-static-buttons">
2404
- {(Array.isArray(card?.buttons) && card.buttons.length ? card.buttons : [{}, {}]).slice(0, 2).map((carouselButton, btnIdx) => (
2405
- <CapLabel
2406
- key={`viber-static-btn-${cardIdx}-${btnIdx}`}
2407
- type="label1"
2408
- className={`viber-carousel-static-button ${btnIdx === 1 ? 'viber-carousel-static-button-secondary' : ''}`}
2409
- >
2410
- {(carouselButton?.title || '').trim()}
2411
- </CapLabel>
2412
- ))}
2413
- </CapRow>
2414
- </CapRow>
2415
- ))}
2416
- </CapRow>
2417
- </CapRow>
2418
- );
2419
- break;
2420
- }
2421
2440
  if (!isEmpty(image)) {
2422
2441
  const { url = '' } = image;
2423
2442
  templateData.url = url;
@@ -2547,13 +2566,14 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2547
2566
  <CapRow type="flex" align="middle">
2548
2567
  {isCardArchiveEligible && this.renderCardSelectionCheckbox({ templateId: template._id, selectedIds: selectedIdsArrayForCard, isDisabled: isAnyArchiveInProgress })}
2549
2568
  <CapLabel className="whatsapp-rcs-template-name">{name}</CapLabel>
2550
- <CapRow type="flex" align="middle" className="rcs-status-container zalo-status-color">
2569
+ {this.state.hostName !== HOST_INFOBIP && <CapRow type="flex" align="middle" className="rcs-status-container zalo-status-color">
2551
2570
  <CapStatus
2552
2571
  type={statusDisplay}
2553
- text={statusDisplay && this.props.intl.formatMessage(rcsMessages?.[`${statusDisplay}_STATUS`])}
2554
- labelType="label3"
2555
- />
2556
- </CapRow>
2572
+ text={statusDisplay && this.props.intl.formatMessage(rcsMessages?.[`${statusDisplay}_STATUS`])}
2573
+ labelType="label3"
2574
+ />
2575
+ </CapRow>
2576
+ }
2557
2577
  </CapRow>
2558
2578
  );
2559
2579
 
@@ -3129,6 +3149,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
3129
3149
  let routeParams = {};
3130
3150
  const {fbAdManager} = this.props;
3131
3151
  const isLibraryMode = this.isEnabledInLibraryModule("callCreateFromProps");
3152
+
3132
3153
  if (!isLibraryMode) {
3133
3154
  timeTracker.startTimer(CHANNEL_CREATE_TRACK_MAPPING[channel]);
3134
3155
  }
@@ -3478,6 +3499,13 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
3478
3499
  this.setState({modeType});
3479
3500
  }
3480
3501
  const { _id: id } = template;
3502
+ const {
3503
+ localTemplatesConfig,
3504
+ fbAdManager,
3505
+ isDltFromRcs,
3506
+ isSmsFallbackFromRcs,
3507
+ onSelectTemplate,
3508
+ } = this.props;
3481
3509
  const type = this.props.location.query.type;
3482
3510
  const module = this.props.location.query.module;
3483
3511
  const isLanguageSupport = (this.props.location.query.isLanguageSupport) ? this.props.location.query.isLanguageSupport : false;
@@ -3563,10 +3591,12 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
3563
3591
  }
3564
3592
  if (this.isEnabledInLibraryModule("callSelectFromProps")) {
3565
3593
  let data = id;
3566
- if (this.props.fbAdManager || this.props.isDltFromRcs) {
3594
+ if (localTemplatesConfig?.useLocalTemplates) {
3595
+ data = template;
3596
+ } else if (fbAdManager || isDltFromRcs || isSmsFallbackFromRcs) {
3567
3597
  data = this.selectTemplate(id);
3568
3598
  }
3569
- this.props.onSelectTemplate(data, this.props.fbAdManager);
3599
+ onSelectTemplate(data, fbAdManager);
3570
3600
  } else {
3571
3601
  timeTracker.startTimer(CHANNEL_EDIT_TRACK_MAPPING[this.state.channel.toLowerCase()]);
3572
3602
  if (this.state.channel.toLowerCase() === 'ebill') {
@@ -4525,9 +4555,14 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4525
4555
  const isWechatEmbedded = !this.props.isFullMode && channel.toUpperCase() === WECHAT;
4526
4556
  const channelLowerCase = (channel || '').toLowerCase();
4527
4557
  const isTraiDltFeature = this.checkDLTfeatureEnable();
4528
-
4529
4558
  const createButton =
4530
- ( (channelLowerCase === WHATSAPP_LOWERCASE || channelLowerCase === RCS_LOWERCASE) && !this.props.isFullMode )
4559
+ (
4560
+ (
4561
+ channelLowerCase === WHATSAPP_LOWERCASE
4562
+ || channelLowerCase === RCS_LOWERCASE
4563
+ )
4564
+ && !this.props.isFullMode
4565
+ )
4531
4566
  ? (
4532
4567
  <CapLink
4533
4568
  onClick={this.openCreativesFullMode}
@@ -4559,17 +4594,22 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4559
4594
  const _renderSelectedIdsArray = _renderSelectedIds && typeof _renderSelectedIds.toJS === 'function' ? _renderSelectedIds.toJS() : (Array.isArray(_renderSelectedIds) ? _renderSelectedIds : []);
4560
4595
  const _renderHasSelection = _isArchivalEnabled && this.props.isFullMode && _renderSelectedIdsArray.length > 0;
4561
4596
 
4562
- const filterContent = (( isfilterContentVisisble || [WECHAT, MOBILE_PUSH, INAPP].includes(this.state.channel.toUpperCase())) && <div className="action-container">
4563
- {isfilterContentVisisble && <CapInput.Search
4564
- className="search-text"
4565
- style={{width: '210px'}}
4566
- placeholder={_isArchivedMode ? this.props.intl.formatMessage(messages.searchArchivedTemplates) : this.props.intl.formatMessage(messages.searchText)}
4567
- value={this.state.searchText}
4568
- onChange={(e) => this.searchTemplate(e.target.value, this.state.channel)}
4569
- disabled={this.checkSearchDisabled()}
4570
- onClear={() => this.searchTemplate('', this.state.channel)}
4571
- onScroll={(e) => e.stopPropagation()}
4572
- />}
4597
+ const useLocalTemplates = this.props.localTemplatesConfig?.useLocalTemplates;
4598
+ const builtFilterContent = ((isfilterContentVisisble || [WECHAT, MOBILE_PUSH, INAPP].includes(this.state.channel.toUpperCase())) && (
4599
+ <div className="action-container">
4600
+ <div className="action-container__toolbar-row">
4601
+ {isfilterContentVisisble ? (
4602
+ <CapInput.Search
4603
+ className="search-text"
4604
+ placeholder={this.props.intl.formatMessage(messages.searchText)}
4605
+ value={this.state.searchText}
4606
+ onChange={(e) => this.searchTemplate(e.target.value, this.state.channel)}
4607
+ onSearch={() => this.searchTemplate(this.state.searchText, this.state.channel)}
4608
+ onClear={() => this.searchTemplate('', this.state.channel)}
4609
+ onScroll={(e) => e.stopPropagation()}
4610
+ disabled={this.checkSearchDisabled()}
4611
+ />
4612
+ ) : null}
4573
4613
  {
4574
4614
  channel.toUpperCase() === WECHAT && <CapRadio.CapRadioGroup className="wechat-filters" defaultValue={wechatFilter} onChange={this.setWechatFilter}>
4575
4615
  <CapRadio.Button value={WECHAT_FILTERS.ALL}><CapLabel type="label2">
@@ -4716,16 +4756,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4716
4756
  )
4717
4757
  }
4718
4758
  <div className="template-listing-header-actions">
4719
- {!_isArchivedMode && !_renderHasSelection && (
4720
- this.state?.channel?.toLowerCase() === WHATSAPP_LOWERCASE && (isWhatsappCountExeeded) ? (
4721
- <CapTooltip title={whatsappCountExceedText}>
4722
- <div className="button-disabled-tooltip-wrapper">
4723
- {createButton}
4724
- </div>
4725
- </CapTooltip>
4726
- )
4727
- : isfilterContentVisisble && !isWechatEmbedded && !this.props.isDltFromRcs && createButton
4728
- )}
4729
4759
  {/* More (⋯) menu: full mode only, not archived mode, not Zalo (no archive support), not when selection active, archive flag enabled */}
4730
4760
  {commonUtil.hasCreativesArchivalEnabled() && !_isArchivedMode && !_renderHasSelection && this.props.isFullMode && this.props.location.query.type !== EMBEDDED && channelLowerCase !== ZALO_LOWERCASE && (
4731
4761
  <CapDropdown
@@ -4748,7 +4778,28 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4748
4778
  </CapDropdown>
4749
4779
  )}
4750
4780
  </div>
4751
- </div>);
4781
+ </div>
4782
+ <div>
4783
+ <div className="action-container__create-row">
4784
+ {
4785
+ isfilterContentVisisble && !isWechatEmbedded && !this.props.isDltFromRcs && !this.props.isSmsFallbackFromRcs && (
4786
+ this.state?.channel?.toLowerCase() === WHATSAPP_LOWERCASE && isWhatsappCountExeeded ? (
4787
+ <CapTooltip title={whatsappCountExceedText}>
4788
+ <div className="button-disabled-tooltip-wrapper">
4789
+ {createButton}
4790
+ </div>
4791
+ </CapTooltip>
4792
+ ) : createButton
4793
+ )
4794
+ }
4795
+ </div>
4796
+ </div>
4797
+ </div>
4798
+ ));
4799
+ const localTemplatesFilterContent = get(this.props, 'localTemplatesConfig.localTemplatesFilterContent', null);
4800
+ const filterContent = (useLocalTemplates && localTemplatesFilterContent) != null
4801
+ ? localTemplatesFilterContent
4802
+ : builtFilterContent;
4752
4803
  let htmlPreviewContent = "";
4753
4804
  if (this.state.channel.toLowerCase() === 'ebill') {
4754
4805
  htmlPreviewContent = this.state.previewTemplate && this.state.previewTemplate.versions && this.state.previewTemplate.versions.base && this.state.previewTemplate.versions.base['ebill-editor'];
@@ -4758,7 +4809,10 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4758
4809
 
4759
4810
 
4760
4811
  const creativesParams = this.getCreativesParams();
4761
- const templates = this.props.TemplatesList || [];
4812
+ const templates = useLocalTemplates
4813
+ ? (this.props.localTemplatesConfig?.localTemplates || [])
4814
+ : (this.props.TemplatesList || []);
4815
+ const isLoadingWhenLocal = useLocalTemplates && !!this.props.localTemplatesConfig?.localTemplatesLoading;
4762
4816
  const {route} = this.props;
4763
4817
  const loadingTipMap = {
4764
4818
  sendingFile: 'uploadingFile',
@@ -4773,9 +4827,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4773
4827
  (deleteRcsTemplateInProgress && 'deletingTemplate') ||
4774
4828
  (this.props.EmailCreate.duplicateTemplateInProgress && 'duplicatingTemplate');
4775
4829
 
4776
- const loadingTip = messages[loadingTipIntl] ? this.props.intl.formatMessage(messages[loadingTipIntl]) : this.props.intl.formatMessage(messages.gettingAllTemplates);
4830
+ const loadingTip = useLocalTemplates && this.props.localTemplatesConfig?.localTemplatesLoadingTip
4831
+ ? this.props.localTemplatesConfig.localTemplatesLoadingTip
4832
+ : (messages[loadingTipIntl] ? this.props.intl.formatMessage(messages[loadingTipIntl]) : this.props.intl.formatMessage(messages.gettingAllTemplates));
4777
4833
  const showNoTemplatesFoundZalo = this.state.channel.toUpperCase() === ZALO && isEmpty(this.state.searchedZaloTemplates) && this.state.searchingZaloTemplate;
4778
- const showNoTemplatesFoundOther = ![ZALO].includes(this.state.channel.toUpperCase()) && isEmpty(this.props.TemplatesList) && !this.props.Templates.getAllTemplatesInProgress && !isEmpty(this.state.searchText);
4834
+ const showNoTemplatesFoundOther = ![ZALO].includes(this.state.channel.toUpperCase()) && isEmpty(templates) && (useLocalTemplates ? !isLoadingWhenLocal : !this.props.Templates.getAllTemplatesInProgress) && (useLocalTemplates || !isEmpty(this.state.searchText));
4779
4835
  const showNoTemplatesFound = showNoTemplatesFoundZalo || showNoTemplatesFoundOther;
4780
4836
 
4781
4837
  return (
@@ -4819,7 +4875,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4819
4875
  />
4820
4876
  ) : null}
4821
4877
 
4822
- {channel.toLowerCase() === RCS_LOWERCASE && !isFullMode ? (
4878
+ {channel.toLowerCase() === RCS_LOWERCASE && !isFullMode && this.state?.hostName !== HOST_INFOBIP ? (
4823
4879
  <CapInfoNote
4824
4880
  message={formatMessage(messages.rcsOnlyApprovedTemplates)}
4825
4881
  />
@@ -4832,22 +4888,22 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4832
4888
  ) : null}
4833
4889
  <CapRow>
4834
4890
  <Pagination
4835
- templateInProgress={
4836
- this.props.Templates.getAllTemplatesInProgress
4837
- }
4891
+ templateInProgress={useLocalTemplates ? isLoadingWhenLocal : this.props.Templates.getAllTemplatesInProgress}
4838
4892
  onPageChange={
4839
- templates.length ? this.onPaginationChange : () => {}
4893
+ templates.length
4894
+ ? (useLocalTemplates ? (this.props.localTemplatesConfig?.localTemplatesOnPageChange || (() => {})) : this.onPaginationChange)
4895
+ : () => {}
4840
4896
  }
4841
4897
  >
4842
4898
  {this.getTemplateDataForGrid({
4843
4899
  previewTemplateId: this.state.zaloPreviewItemId,
4844
- isLoading,
4845
- isInitialLoading,
4900
+ isLoading: useLocalTemplates ? isLoadingWhenLocal : isLoading,
4901
+ isInitialLoading: useLocalTemplates ? isLoadingWhenLocal && templates.length === 0 : isInitialLoading,
4846
4902
  loadingTip,
4847
4903
  channel: this.state.channel,
4848
4904
  templates: this.state.searchingZaloTemplate
4849
4905
  ? this.state.searchedZaloTemplates
4850
- : this.props.TemplatesList,
4906
+ : templates,
4851
4907
  filterContent,
4852
4908
  handlers: {
4853
4909
  handlePreviewClick: this.handlePreviewClick,
@@ -4979,22 +5035,15 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4979
5035
  ? this.props.Templates.selectedWhatsappAccount.name
4980
5036
  : null
4981
5037
  }
4982
- // Pass formData for tag extraction (EMAIL/SMS use nested formData; VIBER uses content object)
5038
+ // Pass formData for EMAIL and SMS channels for tag extraction
4983
5039
  formData={
4984
- (() => {
4985
- const previewContent = this.state.testAndPreviewContent;
4986
- const channelUpper = this.state.channel?.toUpperCase();
4987
- if (!previewContent || typeof previewContent !== 'object') {
4988
- return null;
4989
- }
4990
- if (channelUpper === EMAIL || channelUpper === SMS) {
4991
- return previewContent.formData || null;
4992
- }
4993
- if (channelUpper === VIBER) {
4994
- return previewContent;
4995
- }
4996
- return null;
4997
- })()
5040
+ this.state.testAndPreviewContent &&
5041
+ (this.state.channel?.toUpperCase() === EMAIL ||
5042
+ this.state.channel?.toUpperCase() === SMS) &&
5043
+ typeof this.state.testAndPreviewContent === 'object' &&
5044
+ this.state.testAndPreviewContent?.formData
5045
+ ? this.state.testAndPreviewContent.formData
5046
+ : null
4998
5047
  }
4999
5048
  />
5000
5049
  )}
@@ -5037,6 +5086,15 @@ Templates.propTypes = {
5037
5086
  WebPush: PropTypes.object,
5038
5087
  smsRegister: PropTypes.any,
5039
5088
  isDltFromRcs: PropTypes.bool,
5089
+ isSmsFallbackFromRcs: PropTypes.bool,
5090
+ localTemplatesConfig: PropTypes.shape({
5091
+ useLocalTemplates: PropTypes.bool,
5092
+ localTemplates: PropTypes.arrayOf(PropTypes.object),
5093
+ localTemplatesLoading: PropTypes.bool,
5094
+ localTemplatesLoadingTip: PropTypes.string,
5095
+ localTemplatesFilterContent: PropTypes.node,
5096
+ localTemplatesOnPageChange: PropTypes.func,
5097
+ }),
5040
5098
  };
5041
5099
 
5042
5100
  const mapStateToProps = createStructuredSelector({