@capillarytech/creatives-library 8.0.345-alpha.13 → 8.0.345-alpha.15

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 (138) hide show
  1. package/constants/unified.js +29 -0
  2. package/package.json +1 -1
  3. package/services/api.js +0 -20
  4. package/services/tests/api.test.js +13 -59
  5. package/utils/commonUtils.js +19 -1
  6. package/utils/rcsPayloadUtils.js +92 -0
  7. package/utils/templateVarUtils.js +201 -0
  8. package/utils/tests/templateVarUtils.test.js +204 -0
  9. package/v2Components/CapActionButton/constants.js +7 -0
  10. package/v2Components/CapActionButton/index.js +167 -109
  11. package/v2Components/CapActionButton/index.scss +157 -6
  12. package/v2Components/CapActionButton/messages.js +19 -3
  13. package/v2Components/CapActionButton/tests/index.test.js +41 -17
  14. package/v2Components/CapCustomSkeleton/index.js +1 -1
  15. package/v2Components/CapCustomSkeleton/tests/__snapshots__/index.test.js.snap +12 -12
  16. package/v2Components/CapTagList/index.js +10 -0
  17. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -49
  18. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
  19. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +207 -21
  20. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
  21. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +85 -10
  22. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
  23. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +79 -11
  24. package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
  25. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +160 -15
  26. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js.rej +18 -0
  27. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +341 -76
  28. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +133 -4
  29. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
  30. package/v2Components/CommonTestAndPreview/constants.js +38 -2
  31. package/v2Components/CommonTestAndPreview/index.js +676 -186
  32. package/v2Components/CommonTestAndPreview/messages.js +49 -3
  33. package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
  34. package/v2Components/CommonTestAndPreview/sagas.js +15 -6
  35. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +308 -284
  36. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +231 -65
  37. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
  38. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
  39. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
  40. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +34 -13
  41. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
  42. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
  43. package/v2Components/CommonTestAndPreview/tests/index.test.js +132 -4
  44. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
  45. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
  46. package/v2Components/FormBuilder/index.js +8 -10
  47. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +87 -0
  48. package/v2Components/SmsFallback/constants.js +73 -0
  49. package/v2Components/SmsFallback/index.js +955 -0
  50. package/v2Components/SmsFallback/index.scss +265 -0
  51. package/v2Components/SmsFallback/messages.js +78 -0
  52. package/v2Components/SmsFallback/smsFallbackUtils.js +118 -0
  53. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
  54. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
  55. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
  56. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +197 -0
  57. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +277 -0
  58. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
  59. package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
  60. package/v2Components/TemplatePreview/_templatePreview.scss +33 -23
  61. package/v2Components/TemplatePreview/constants.js +2 -0
  62. package/v2Components/TemplatePreview/index.js +143 -28
  63. package/v2Components/TemplatePreview/tests/index.test.js +142 -0
  64. package/v2Components/TestAndPreviewSlidebox/index.js +13 -1
  65. package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
  66. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
  67. package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
  68. package/v2Components/VarSegmentMessageEditor/index.js +125 -0
  69. package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
  70. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +43 -0
  71. package/v2Containers/CreativesContainer/SlideBoxContent.js +36 -4
  72. package/v2Containers/CreativesContainer/SlideBoxFooter.js +11 -4
  73. package/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
  74. package/v2Containers/CreativesContainer/constants.js +9 -0
  75. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +67 -0
  76. package/v2Containers/CreativesContainer/index.js +300 -108
  77. package/v2Containers/CreativesContainer/index.scss +51 -1
  78. package/v2Containers/CreativesContainer/messages.js +0 -4
  79. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
  80. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +78 -34
  81. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +79 -16
  82. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
  83. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +357 -98
  84. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -18
  85. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
  86. package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
  87. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
  88. package/v2Containers/Rcs/constants.js +119 -8
  89. package/v2Containers/Rcs/index.js +2379 -807
  90. package/v2Containers/Rcs/index.js.rej +1336 -0
  91. package/v2Containers/Rcs/index.scss +276 -6
  92. package/v2Containers/Rcs/index.scss.rej +74 -0
  93. package/v2Containers/Rcs/messages.js +38 -3
  94. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +225 -0
  95. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +98018 -70073
  96. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
  97. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap.rej +128 -0
  98. package/v2Containers/Rcs/tests/index.test.js +152 -121
  99. package/v2Containers/Rcs/tests/mockData.js +38 -0
  100. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +318 -0
  101. package/v2Containers/Rcs/tests/utils.test.js +646 -30
  102. package/v2Containers/Rcs/utils.js +478 -11
  103. package/v2Containers/Sms/Create/index.js +100 -40
  104. package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
  105. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  106. package/v2Containers/SmsTrai/Create/index.js +9 -4
  107. package/v2Containers/SmsTrai/Edit/constants.js +2 -0
  108. package/v2Containers/SmsTrai/Edit/index.js +636 -130
  109. package/v2Containers/SmsTrai/Edit/index.scss +121 -0
  110. package/v2Containers/SmsTrai/Edit/messages.js +14 -4
  111. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4328 -2375
  112. package/v2Containers/SmsWrapper/index.js +37 -8
  113. package/v2Containers/TagList/index.js +6 -0
  114. package/v2Containers/Templates/ChannelTypeIllustration.js +6 -23
  115. package/v2Containers/Templates/TemplatesActionBar.js +101 -0
  116. package/v2Containers/Templates/_templates.scss +181 -126
  117. package/v2Containers/Templates/actions.js +11 -36
  118. package/v2Containers/Templates/constants.js +2 -23
  119. package/v2Containers/Templates/index.js +142 -333
  120. package/v2Containers/Templates/messages.js +0 -68
  121. package/v2Containers/Templates/reducer.js +0 -68
  122. package/v2Containers/Templates/sagas.js +55 -98
  123. package/v2Containers/Templates/selectors.js +0 -12
  124. package/v2Containers/Templates/tests/ChannelTypeIllustration.test.js +0 -12
  125. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
  126. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1042 -1256
  127. package/v2Containers/Templates/tests/index.test.js +0 -6
  128. package/v2Containers/Templates/tests/reducer.test.js +0 -178
  129. package/v2Containers/Templates/tests/sagas.test.js +200 -436
  130. package/v2Containers/Templates/tests/selector.test.js +0 -32
  131. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -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/Whatsapp/index.js +3 -20
  137. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
  138. package/v2Containers/Assets/images/archive_Empty_Illustration.svg +0 -9
@@ -50,7 +50,9 @@ import {
50
50
  CapStatus,
51
51
  CapColoredTag,
52
52
  CapSpin,
53
- CapCheckbox,
53
+ CapCard,
54
+ CapColumn,
55
+ CapCarousel
54
56
  } from "@capillarytech/cap-ui-library";
55
57
  import { makeSelectTemplates, makeSelectTemplatesResponse } from './selectors';
56
58
  import { makeSelectCreate as makeSelectCreateSms } from '../Sms/Create/selectors';
@@ -114,15 +116,23 @@ import {
114
116
  STATUS as WHATSAPP_STATUS,
115
117
  WHATSAPP_STATUSES,
116
118
  HOST_GUPSHUP,
119
+ HOST_HAPTIC,
117
120
  CATEGORY_OPTIONS_MAP,
121
+ HOST_TWILIO,
122
+ TWILIO_CATEGORY_OPTIONS,
123
+ KARIX_GUPSHUP_CATEGORY_OPTIONS,
124
+ ICS_CATEGORY_OPTIONS,
125
+ HAPTIC_CATEGORY_OPTIONS,
126
+ HOST_ICS,
118
127
  IMAGE,
119
128
  VIDEO,
129
+ GIF,
120
130
  } from '../Whatsapp/constants';
121
- import { INAPP_LAYOUT_DETAILS, INAPP_MESSAGE_LAYOUT_TYPES } from '../InApp/constants';
131
+ import { INAPP_LAYOUT_DETAILS, INAPP_MESSAGE_LAYOUT_TYPES, INAPP_MEDIA_TYPES, BIG_HTML, ANDROID, IOS } from '../InApp/constants';
122
132
  import { ZALO_STATUS_OPTIONS, ZALO_STATUSES } from '../Zalo/constants';
123
133
  import { getWhatsappContent, getWhatsappStatus, getWhatsappCategory, getWhatsappCta, getWhatsappQuickReply, getWhatsappAutoFill, getWhatsappCarouselButtonView } from '../Whatsapp/utils';
124
134
  import { getRCSContent } from '../Rcs/utils';
125
- import {RCS_STATUSES} from '../Rcs/constants';
135
+ import { RCS_STATUSES, HOST_INFOBIP } from '../Rcs/constants';
126
136
  import zaloMessages from '../Zalo/messages';
127
137
  import rcsMessages from '../Rcs/messages';
128
138
  import inAppMessages from '../InApp/messages';
@@ -135,7 +145,7 @@ import {CREATIVE} from '../Facebook/constants';
135
145
  import videoPlay from '../../assets/videoPlay.svg';
136
146
  import whatsappImageEmptyPreview from '../../v2Components/TemplatePreview/assets/images/empty_image_preview.svg';
137
147
  import whatsappVideoEmptyPreview from '../../v2Components/TemplatePreview/assets/images/empty_video_preview.svg';
138
- import { CAP_SPACE_16, CAP_G08, CAP_G05, CAP_SPACE_08, CAP_SPACE_12 } from '@capillarytech/cap-ui-library/styled/variables';
148
+ import { CAP_SPACE_16 } from '@capillarytech/cap-ui-library/styled/variables';
139
149
  import { GA } from '@capillarytech/cap-ui-utils';
140
150
  import { MAPP_SDK } from '../InApp/constants';
141
151
  import injectReducer from '../../utils/injectReducer';
@@ -144,6 +154,7 @@ import { compose } from 'redux';
144
154
  import { v2TemplateSaga } from './sagas';
145
155
  import injectSaga from '../../utils/injectSaga';
146
156
  import { DAEMON } from '@capillarytech/vulcan-react-sdk/utils/sagaInjectorTypes';
157
+ import { Rcs } from '../Rcs';
147
158
  import { makeSelectRcs } from '../Rcs/selectors';
148
159
  import { getRcsStatusType } from '../Rcs/utils';
149
160
  import { makeSelectWebPush } from '../WebPush/selectors';
@@ -426,9 +437,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
426
437
  channel = '';
427
438
  }
428
439
  this.setState({ channel, activeMode });
429
- // Always reset archive mode and selection when mounting a new channel
430
- this.props.actions.setArchivedMode(false);
431
- this.props.actions.clearTemplateSelection();
432
440
  // Clear templates when entering account selection mode to prevent showing old channel templates
433
441
  if (activeMode === ACCOUNT_SELECTION_MODE) {
434
442
  this.props.actions.resetTemplate();
@@ -460,7 +468,13 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
460
468
  if (this.props.location.query.type === 'embedded') {
461
469
  this.props.actions.resetAccount();
462
470
  }
463
- if (['line', VIBER_CHANNEL, FACEBOOK_CHANNEL, 'sms', 'email', 'ebill'].includes((this.state.channel || '').toLowerCase())) {
471
+ // When using local templates (e.g. SMS fallback selector), do not fetch from API or we overwrite global store and break background RCS list
472
+ const useLocalTemplates = get(
473
+ this.props,
474
+ 'localTemplatesConfig.useLocalTemplates',
475
+ get(this.props, 'useLocalTemplates', false),
476
+ );
477
+ if (!useLocalTemplates && ['line', VIBER_CHANNEL, FACEBOOK_CHANNEL, 'sms', 'email', 'ebill'].includes((this.state.channel || '').toLowerCase())) {
464
478
  const queryParams = {
465
479
  // name: this.state.searchText,
466
480
  // sortBy: this.state.sortBy,
@@ -973,8 +987,16 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
973
987
 
974
988
  componentWillUnmount() {
975
989
  window.removeEventListener("message", this.handleFrameTasks);
976
- this.props.actions.resetTemplateStoreData();
977
- this.props.globalActions.clearMetaEntities();
990
+ // When using local templates (e.g. SMS fallback selector), do not clear global store or background RCS list is wiped
991
+ const useLocalTemplates = get(
992
+ this.props,
993
+ 'localTemplatesConfig.useLocalTemplates',
994
+ get(this.props, 'useLocalTemplates', false),
995
+ );
996
+ if (!useLocalTemplates) {
997
+ this.props.actions.resetTemplateStoreData();
998
+ this.props.globalActions.clearMetaEntities();
999
+ }
978
1000
  // Clear any pending timeouts to prevent memory leaks
979
1001
  if (this._clearEditTimeout) {
980
1002
  clearTimeout(this._clearEditTimeout);
@@ -1587,11 +1609,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1587
1609
  let queryParams = params || {};
1588
1610
  let page = this.state.page;
1589
1611
  const { activeMode } = this.state;
1590
- // Archive filter — use explicit param if provided (props may not have updated yet due to async dispatch)
1591
- if (!queryParams.archiveStatus) {
1592
- const archiveFilter = get(this.props, 'Templates.archiveFilter', 'active');
1593
- queryParams.archiveStatus = archiveFilter;
1594
- }
1595
1612
  if (activeMode === ACCOUNT_SELECTION_MODE) {
1596
1613
  this.setTemplatesMode();
1597
1614
  }
@@ -1764,12 +1781,20 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1764
1781
  }
1765
1782
 
1766
1783
  filterRcsTemplates = (templates) => {
1767
- let { selectedRcsStatus } = this.state;
1768
- selectedRcsStatus = !this.props.isFullMode ? RCS_STATUSES.approved : '';
1769
- if (selectedRcsStatus) {
1770
- return templates?.filter((template) => template?.versions?.base?.content?.RCS?.rcsContent?.cardContent?.[0]?.Status === selectedRcsStatus);
1784
+ const selectedRcsAccountName = this.props?.Templates?.selectedRcsAccount?.name || '';
1785
+ const hostName = this.state?.hostName;
1786
+ let nextTemplates = templates || [];
1787
+ if (selectedRcsAccountName) {
1788
+ nextTemplates = nextTemplates.filter(
1789
+ (t) => get(t, 'versions.base.content.RCS.rcsContent.accountName', '') === selectedRcsAccountName
1790
+ );
1771
1791
  }
1772
- return templates;
1792
+ if (!this.props.isFullMode && hostName !== HOST_INFOBIP) {
1793
+ return nextTemplates.filter(
1794
+ (t) => get(t, 'versions.base.content.RCS.rcsContent.cardContent[0].Status', 'unavailable') === RCS_STATUSES.approved
1795
+ );
1796
+ }
1797
+ return nextTemplates;
1773
1798
  }
1774
1799
 
1775
1800
  filterZaloTemplates = (templates) => {
@@ -1858,9 +1883,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1858
1883
  const currentChannel = channel.toUpperCase();
1859
1884
  const {channel: stateChannel} = this.state;
1860
1885
  const channelLowerCase = stateChannel.toLowerCase();
1861
- const _selectedIds = get(this.props, 'Templates.selectedTemplateIds', []);
1862
- const _selectedIdsArray = _selectedIds && typeof _selectedIds.toJS === 'function' ? _selectedIds.toJS() : (Array.isArray(_selectedIds) ? _selectedIds : []);
1863
- const hasSelection = this.props.isFullMode && _selectedIdsArray.length > 0;
1864
1886
  let filteredTemplates = templates;
1865
1887
  let isTraiDltFeature = false;
1866
1888
  switch (currentChannel) {
@@ -1900,26 +1922,10 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1900
1922
  const iosBodyType = get(template, 'versions.base.content.IOS.bodyType');
1901
1923
  const inappBodyType = androidBodyType || iosBodyType;
1902
1924
  const isZaloPreviewLoading = previewTemplateId === template?._id;
1903
- const selectedIdsForCard = get(this.props, 'Templates.selectedTemplateIds', []);
1904
- const selectedIdsArrayForCard = selectedIdsForCard.toJS ? selectedIdsForCard.toJS() : selectedIdsForCard;
1905
- // Archive eligibility per template: Zalo never; WhatsApp/RCS not when pending/awaiting
1906
- const cardWhatsappStatus = get(template, `versions.base.content.${WHATSAPP_LOWERCASE}.status`, '');
1907
- const cardRcsStatus = get(template, 'versions.base.content.RCS.rcsContent.cardContent[0].Status', '');
1908
- const isCardArchiveEligible = currentChannel !== ZALO &&
1909
- !(currentChannel === WHATSAPP && [WHATSAPP_STATUSES.awaitingApproval, WHATSAPP_STATUSES.pending, WHATSAPP_STATUSES.unsubmitted].includes(cardWhatsappStatus)) &&
1910
- !(currentChannel === RCS && [RCS_STATUSES.awaitingApproval, RCS_STATUSES.pending].includes(cardRcsStatus));
1911
1925
  const templateData = {
1912
1926
  key: `${currentChannel}-card-${template?.name}`,
1913
1927
  title: (
1914
- <span title={template?.name} style={{ display: 'flex', alignItems: 'center' }}>
1915
- {this.props.isFullMode && this.props.location.query.type !== 'embedded' && isCardArchiveEligible && (
1916
- <CapCheckbox
1917
- checked={selectedIdsArrayForCard.includes(template._id)}
1918
- onChange={() => this.props.actions.toggleTemplateSelection(template._id)}
1919
- onClick={(e) => e.stopPropagation()}
1920
- style={{ marginRight: CAP_SPACE_08, flexShrink: 0 }}
1921
- />
1922
- )}
1928
+ <span title={template?.name}>
1923
1929
  {template?.name}
1924
1930
  {currentChannel === INAPP && (
1925
1931
  <CapRow>
@@ -1963,7 +1969,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1963
1969
  style={{ marginRight: "16px" }}
1964
1970
  type="eye"
1965
1971
  onClick={() => {
1966
- if (!this.props.isFullMode || this.props.isDltFromRcs) {
1972
+ if (!this.props.isFullMode || this.props.isDltFromRcs || this.props.isSmsFallbackFromRcs) {
1967
1973
  if (!get(template, "versions.base.content.zalo.previewUrl", "")) {
1968
1974
  this.setState({ zaloPreviewItemId: template?._id });
1969
1975
  }
@@ -2040,7 +2046,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2040
2046
  {![WECHAT, WHATSAPP, ZALO].includes(
2041
2047
  this.state.channel.toUpperCase()
2042
2048
  ) &&
2043
- !isTraiDltFeature && !template.isArchived && (
2049
+ !isTraiDltFeature && (
2044
2050
  <CapMenu.Item
2045
2051
  className={`duplicate-${channelLowerCase}`}
2046
2052
  onClick={() => this.duplicateTemplate(template)}
@@ -2048,42 +2054,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2048
2054
  <FormattedMessage {...messages.duplicateButton} />
2049
2055
  </CapMenu.Item>
2050
2056
  )}
2051
- {/* Archive/Unarchive menu item (full mode only, not for Zalo, not for WhatsApp/RCS awaiting/pending) */}
2052
- {(() => {
2053
- const channelUp = this.state.channel.toUpperCase();
2054
- const isArchiveEligible = channelUp !== ZALO &&
2055
- !(channelUp === WHATSAPP && [WHATSAPP_STATUSES.awaitingApproval, WHATSAPP_STATUSES.pending, WHATSAPP_STATUSES.unsubmitted].includes(status)) &&
2056
- !(channelUp === RCS && [RCS_STATUSES.awaitingApproval, RCS_STATUSES.pending].includes(rcsStatus));
2057
- if (!isArchiveEligible) return null;
2058
- return !template.isArchived ? (
2059
- <CapMenu.Item
2060
- className={`archive-${channelLowerCase}`}
2061
- onClick={() => {
2062
- this.showArchiveConfirm({
2063
- title: this.props.intl.formatMessage(messages.archiveTemplates),
2064
- onConfirm: () => this.props.actions.archiveTemplate(this.state.channel, template._id, template.name),
2065
- count: 1,
2066
- });
2067
- }}
2068
- >
2069
- <FormattedMessage {...messages.archiveButton} />
2070
- </CapMenu.Item>
2071
- ) : (
2072
- <CapMenu.Item
2073
- className={`unarchive-${channelLowerCase}`}
2074
- onClick={() => {
2075
- this.showArchiveConfirm({
2076
- title: this.props.intl.formatMessage(messages.unarchiveTemplates),
2077
- onConfirm: () => this.props.actions.unarchiveTemplate(this.state.channel, template._id, template.name),
2078
- count: 1,
2079
- isUnarchive: true,
2080
- });
2081
- }}
2082
- >
2083
- <FormattedMessage {...messages.unarchiveButton} />
2084
- </CapMenu.Item>
2085
- );
2086
- })()}
2087
2057
  {/* Delete/Unmap menu item */}
2088
2058
  {(!(
2089
2059
  this.state.channel.toUpperCase() === WHATSAPP &&
@@ -2364,15 +2334,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2364
2334
  case WHATSAPP: {
2365
2335
  const { whatsappImageSrc = '', templateMsg, docPreview, whatsappVideoPreviewImg = '', templateHeaderPreview, templateFooterPreview, carouselData = [] } = getWhatsappContent(template);
2366
2336
  templateData.title = (
2367
- <CapRow type="flex" align="middle">
2368
- {this.props.isFullMode && this.props.location.query.type !== 'embedded' && isCardArchiveEligible && (
2369
- <CapCheckbox
2370
- checked={selectedIdsArrayForCard.includes(template._id)}
2371
- onChange={() => this.props.actions.toggleTemplateSelection(template._id)}
2372
- onClick={(e) => e.stopPropagation()}
2373
- style={{ marginRight: CAP_SPACE_08, flexShrink: 0 }}
2374
- />
2375
- )}
2337
+ <CapRow>
2376
2338
  <CapLabel className="whatsapp-rcs-template-name">{template?.name}</CapLabel>
2377
2339
  <WhatsappStatusContainer template={template} />
2378
2340
  </CapRow>
@@ -2465,23 +2427,16 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2465
2427
  const name = get(template, "name", "");
2466
2428
  const statusDisplay=getRcsStatusType(status);
2467
2429
  templateData.title = (
2468
- <CapRow type="flex" align="middle">
2469
- {this.props.isFullMode && this.props.location.query.type !== 'embedded' && isCardArchiveEligible && (
2470
- <CapCheckbox
2471
- checked={selectedIdsArrayForCard.includes(template._id)}
2472
- onChange={() => this.props.actions.toggleTemplateSelection(template._id)}
2473
- onClick={(e) => e.stopPropagation()}
2474
- style={{ marginRight: CAP_SPACE_08, flexShrink: 0 }}
2475
- />
2476
- )}
2430
+ <CapRow>
2477
2431
  <CapLabel className="whatsapp-rcs-template-name">{name}</CapLabel>
2478
- <CapRow type="flex" align="middle" className="rcs-status-container zalo-status-color">
2432
+ {this.state.hostName !== HOST_INFOBIP && <CapRow type="flex" align="middle" className="rcs-status-container zalo-status-color">
2479
2433
  <CapStatus
2480
2434
  type={statusDisplay}
2481
- text={statusDisplay && this.props.intl.formatMessage(rcsMessages?.[`${statusDisplay}_STATUS`])}
2482
- labelType="label3"
2483
- />
2484
- </CapRow>
2435
+ text={statusDisplay && this.props.intl.formatMessage(rcsMessages?.[`${statusDisplay}_STATUS`])}
2436
+ labelType="label3"
2437
+ />
2438
+ </CapRow>
2439
+ }
2485
2440
  </CapRow>
2486
2441
  );
2487
2442
 
@@ -2597,7 +2552,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2597
2552
 
2598
2553
  //no templates available
2599
2554
  const showIllustrationForChannel = (forChannel) => {
2600
- return forChannel === channelLowerCase && isEmpty(templates) && isEmpty(this.state.searchText) && !isLoading && !get(this.props, 'Templates.isArchivedMode', false);
2555
+ return forChannel === channelLowerCase && isEmpty(templates) && isEmpty(this.state.searchText) && !isLoading;
2601
2556
  }
2602
2557
  //when filters applied not matching templates
2603
2558
  const filteredEmptyAndFullModeAs = (fullModeValue) => {
@@ -2623,49 +2578,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2623
2578
 
2624
2579
  const noLoaderAndSearchText = isEmpty(this.state.searchText) && !isLoading;
2625
2580
 
2626
- const isArchivedModeLocal = get(this.props, 'Templates.isArchivedMode', false);
2627
- const selectedIdsLocal = get(this.props, 'Templates.selectedTemplateIds', []);
2628
- const selectedIdsArrayLocal = selectedIdsLocal && typeof selectedIdsLocal.toJS === 'function' ? selectedIdsLocal.toJS() : (Array.isArray(selectedIdsLocal) ? selectedIdsLocal : []);
2629
- const selectedCountLocal = selectedIdsArrayLocal.length;
2630
- const hasSelectionLocal = this.props.isFullMode && selectedCountLocal > 0;
2631
-
2632
- return (<div>
2581
+ return (<div>
2633
2582
  {[WECHAT, MOBILE_PUSH, WEBPUSH, INAPP, WHATSAPP, ZALO, RCS].includes(currentChannel) && this.showAccountName()}
2634
- <CapRow type="flex" align="middle" justify="space-between" className="filter-row">
2635
- <div className="filter-row-content">{filterContent}</div>
2636
- {hasSelectionLocal && (
2637
- <CapRow type="flex" align="middle" className="bulk-selection-bar">
2638
- <CapLabel type="label2">
2639
- {this.props.intl.formatMessage(messages.templatesSelected, { count: selectedCountLocal })}
2640
- </CapLabel>
2641
- <CapButton
2642
- type="primary"
2643
- prefix={<CapIcon type="archive" size="l" />}
2644
- onClick={() => {
2645
- this.showArchiveConfirm({
2646
- title: this.props.intl.formatMessage(isArchivedModeLocal ? messages.unarchiveTemplates : messages.archiveTemplates),
2647
- onConfirm: () => {
2648
- if (isArchivedModeLocal) {
2649
- this.props.actions.bulkUnarchiveTemplates(this.state.channel, selectedIdsArrayLocal);
2650
- } else {
2651
- this.props.actions.bulkArchiveTemplates(this.state.channel, selectedIdsArrayLocal);
2652
- }
2653
- },
2654
- count: selectedCountLocal,
2655
- isUnarchive: isArchivedModeLocal,
2656
- });
2657
- }}
2658
- >
2659
- <span className="archive-btn-label">
2660
- <FormattedMessage {...(isArchivedModeLocal ? messages.unarchiveButton : messages.archiveButton)} />
2661
- </span>
2662
- </CapButton>
2663
- <CapButton type="secondary" onClick={() => this.props.actions.clearTemplateSelection()}>
2664
- <FormattedMessage {...messages.archiveConfirmCancel} />
2665
- </CapButton>
2666
- </CapRow>
2667
- )}
2668
- </CapRow>
2583
+ {filterContent}
2669
2584
  {[WHATSAPP, ZALO, INAPP,RCS].includes(currentChannel) && this.selectedFilters()}
2670
2585
  {<div>
2671
2586
  {!isEmpty(filteredTemplates) || !isEmpty(this.state.searchText) || !isEmpty(this.props.Templates.templateError) ? (
@@ -2678,7 +2593,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2678
2593
  fbType={"list"}
2679
2594
  />
2680
2595
  </div>)
2681
- : [SMS_LOWERCASE, EMAIL_LOWERCASE].includes(this.state.channel.toLowerCase()) && !isLoading && !get(this.props, 'Templates.isArchivedMode', false) && this.getSmsEmailIllustration()
2596
+ : [SMS_LOWERCASE, EMAIL_LOWERCASE].includes(this.state.channel.toLowerCase()) && !isLoading && this.getSmsEmailIllustration()
2682
2597
  }
2683
2598
 
2684
2599
  {(this.state.selectedAccount === '' && isEmpty(this.props.Templates.selectedWeChatAccount)) && ([WECHAT_LOWERCASE, MOBILE_PUSH_LOWERCASE, INAPP_LOWERCASE].includes(this.state.channel.toLowerCase())) &&
@@ -2702,7 +2617,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2702
2617
  </div>
2703
2618
  )
2704
2619
  }
2705
- {(showWhatsappIllustration || showZaloIllustration) && !get(this.props, 'Templates.isArchivedMode', false) && (
2620
+ {(showWhatsappIllustration || showZaloIllustration) && (
2706
2621
  noLoaderAndSearchText &&
2707
2622
  <div style={this.isFullMode() ? { height: "calc(100vh - 325px)", overflow: 'auto' } : {}}>
2708
2623
  {noWhatsappZaloTemplates && <ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={currentChannel} hostName={this.state?.hostName}/>}
@@ -2743,16 +2658,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2743
2658
  <ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={currentChannel} hostName={this.state?.hostName}/>
2744
2659
  </div>
2745
2660
  }
2746
- {get(this.props, 'Templates.isArchivedMode', false) && isEmpty(templates) && !isLoading && !getAllTemplatesInProgress && isEmpty(this.state.searchText) && (
2747
- <div className={this.isFullMode() ? 'illustration-scroll-wrapper' : ''}>
2748
- <ChannelTypeIllustration
2749
- isFullMode={this.props.isFullMode}
2750
- createTemplate={this.createTemplate}
2751
- currentChannel={currentChannel}
2752
- isArchivedMode
2753
- />
2754
- </div>
2755
- )}
2756
2661
  {<CapCustomSkeleton loader={isInitialLoading && (isLoading || getAllTemplatesInProgress)} />}
2757
2662
  {<CapPageSpinner spinning={!isInitialLoading && (isLoading || getAllTemplatesInProgress)} />}
2758
2663
  </div>
@@ -3055,6 +2960,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
3055
2960
  let routeParams = {};
3056
2961
  const {fbAdManager} = this.props;
3057
2962
  const isLibraryMode = this.isEnabledInLibraryModule("callCreateFromProps");
2963
+
3058
2964
  if (!isLibraryMode) {
3059
2965
  timeTracker.startTimer(CHANNEL_CREATE_TRACK_MAPPING[channel]);
3060
2966
  }
@@ -3396,14 +3302,17 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
3396
3302
  };
3397
3303
 
3398
3304
  handleEditClick(e, template, modeType, path, options) {
3399
- if (template && template.isArchived) {
3400
- CapNotification.error({ message: this.props.intl.formatMessage(messages.cannotEditArchivedTemplate) });
3401
- return;
3402
- }
3403
3305
  if (modeType && modeType !== undefined) {
3404
3306
  this.setState({modeType});
3405
3307
  }
3406
3308
  const { _id: id } = template;
3309
+ const {
3310
+ localTemplatesConfig,
3311
+ fbAdManager,
3312
+ isDltFromRcs,
3313
+ isSmsFallbackFromRcs,
3314
+ onSelectTemplate,
3315
+ } = this.props;
3407
3316
  const type = this.props.location.query.type;
3408
3317
  const module = this.props.location.query.module;
3409
3318
  const isLanguageSupport = (this.props.location.query.isLanguageSupport) ? this.props.location.query.isLanguageSupport : false;
@@ -3489,10 +3398,12 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
3489
3398
  }
3490
3399
  if (this.isEnabledInLibraryModule("callSelectFromProps")) {
3491
3400
  let data = id;
3492
- if (this.props.fbAdManager || this.props.isDltFromRcs) {
3401
+ if (localTemplatesConfig?.useLocalTemplates) {
3402
+ data = template;
3403
+ } else if (fbAdManager || isDltFromRcs || isSmsFallbackFromRcs) {
3493
3404
  data = this.selectTemplate(id);
3494
3405
  }
3495
- this.props.onSelectTemplate(data, this.props.fbAdManager);
3406
+ onSelectTemplate(data, fbAdManager);
3496
3407
  } else {
3497
3408
  timeTracker.startTimer(CHANNEL_EDIT_TRACK_MAPPING[this.state.channel.toLowerCase()]);
3498
3409
  if (this.state.channel.toLowerCase() === 'ebill') {
@@ -3581,33 +3492,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
3581
3492
  this.setState({showModal: false});
3582
3493
  }
3583
3494
 
3584
- // Shared helper for archive/unarchive confirm modals:
3585
- // - no icon, lighter overlay, Confirm button on left (primary), Cancel on right
3586
- showArchiveConfirm = ({ title, content, onConfirm, count = 1, isUnarchive = false }) => {
3587
- const { intl } = this.props;
3588
- const confirmText = intl.formatMessage(messages.archiveConfirmOk);
3589
- const cancelText = intl.formatMessage(messages.archiveConfirmCancel);
3590
- // Derive content from count if not explicitly provided
3591
- const resolvedContent = content || (isUnarchive
3592
- ? intl.formatMessage(count > 1 ? messages.unarchiveTemplateContent : messages.unarchiveTemplateSingleContent)
3593
- : intl.formatMessage(count > 1 ? messages.archiveTemplateContent : messages.archiveTemplateSingleContent));
3594
- // AntD v3 footer order is [cancelButton][okButton]. Swap text+handler so
3595
- // "Confirm" (primary) appears on the left and "Cancel" (default) on the right.
3596
- CapModal.confirm({
3597
- title,
3598
- content: resolvedContent,
3599
- icon: ' ',
3600
- className: 'archive-confirm-modal',
3601
- maskStyle: { backgroundColor: 'rgba(0, 0, 0, 0.25)' },
3602
- cancelText: confirmText,
3603
- okText: cancelText,
3604
- cancelButtonProps: { type: 'primary' },
3605
- okButtonProps: { type: 'default' },
3606
- onCancel: (close) => { onConfirm(); close(); },
3607
- // onOk (the right "Cancel" button) just closes the modal — default behaviour
3608
- });
3609
- }
3610
-
3611
3495
  populateTemplatesList = (data, blankTemplateRequired, layoutSelection) => {
3612
3496
  if (!data) {
3613
3497
  return [];
@@ -3757,78 +3641,20 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
3757
3641
  deleteOption = this.props.intl.formatMessage(messages.unMapButton);
3758
3642
  }
3759
3643
  if (!layoutSelection) {
3760
- // Determine archive eligibility for this template:
3761
- // - Zalo: never eligible
3762
- // - WhatsApp: not eligible when status is awaitingApproval / pending / unsubmitted
3763
- // - RCS: not eligible when status is awaitingApproval / pending
3764
- const templateWhatsappStatus = get(template, `versions.base.content.${WHATSAPP_LOWERCASE}.status`, '');
3765
- const templateRcsStatus = get(template, 'versions.base.content.RCS.rcsContent.cardContent[0].Status', '');
3766
- const channelUpCase = this.state.channel.toUpperCase();
3767
- const isTemplateArchiveEligible = channelUpCase !== ZALO &&
3768
- !(channelUpCase === WHATSAPP && [WHATSAPP_STATUSES.awaitingApproval, WHATSAPP_STATUSES.pending, WHATSAPP_STATUSES.unsubmitted].includes(templateWhatsappStatus)) &&
3769
- !(channelUpCase === RCS && [RCS_STATUSES.awaitingApproval, RCS_STATUSES.pending].includes(templateRcsStatus));
3770
-
3771
- // Checkbox on card header (full mode only, only for archive-eligible templates)
3772
- if (this.props.isFullMode && this.props.location.query.type !== 'embedded' && isTemplateArchiveEligible) {
3773
- const selectedIds = get(this.props, 'Templates.selectedTemplateIds', []);
3774
- const selectedIdsArray = selectedIds.toJS ? selectedIds.toJS() : selectedIds;
3775
- temp.cardTop = (
3776
- <div className="template-card-top-bar">
3777
- <CapCheckbox
3778
- checked={selectedIdsArray.includes(template._id)}
3779
- onChange={() => this.props.actions.toggleTemplateSelection(template._id)}
3780
- onClick={(e) => e.stopPropagation()}
3781
- />
3782
- </div>
3783
- );
3784
- }
3785
-
3786
3644
  temp.footer = (
3787
3645
  <div className="footer-container">
3788
3646
  <div className="card-title">
3789
3647
  <span className="template-name" style={{ fontWeight: `${this.state.channel.toLowerCase() === 'wechat' ? '400' : '600'}` }}>
3790
3648
  { template && template.versions && template.versions.history && template.versions.history.length > 1 && this.state.channel.toLowerCase() !== 'mobilepush' && <i style={{fontSize: '16px', margin: '0 8px 0 0', verticalAlign: 'middle'}} className="material-icons">filter_none</i>}
3791
3649
  {template?.name}
3792
- {template.isArchived && <CapColoredTag tagColor={CAP_G08} tagTextColor={CAP_G05} className="archived-tag">{this.props.intl.formatMessage(messages.archivedTag)}</CapColoredTag>}
3793
3650
  </span>
3794
3651
  {this.props.location.query.type !== 'embedded' && <CapPopover
3795
3652
  trigger="click"
3796
3653
  content={
3797
3654
  <div className="popover-content">
3798
- {this.state.channel !== 'wechat' && !template.isArchived && <div className="popover-action-container">
3655
+ {this.state.channel !== 'wechat' && <div className="popover-action-container">
3799
3656
  <span onClick={() => this.duplicateTemplate(template)} className="popover-action" style={{cursor: 'pointer', padding: '8px 0px'}}>{this.props.intl.formatMessage(messages.duplicateButton)}</span>
3800
3657
  </div>}
3801
- {this.props.isFullMode && isTemplateArchiveEligible && !template.isArchived && <div className="popover-action-container">
3802
- <span
3803
- onClick={() => {
3804
- this.showArchiveConfirm({
3805
- title: this.props.intl.formatMessage(messages.archiveTemplates),
3806
- onConfirm: () => this.props.actions.archiveTemplate(this.state.channel, template._id, template.name),
3807
- count: 1,
3808
- });
3809
- }}
3810
- className="popover-action popover-archive-action"
3811
- >
3812
- <CapIcon type="archive" size="l" />
3813
- {this.props.intl.formatMessage(messages.archiveButton)}
3814
- </span>
3815
- </div>}
3816
- {this.props.isFullMode && isTemplateArchiveEligible && template.isArchived && <div className="popover-action-container">
3817
- <span
3818
- onClick={() => {
3819
- this.showArchiveConfirm({
3820
- title: this.props.intl.formatMessage(messages.unarchiveTemplates),
3821
- onConfirm: () => this.props.actions.unarchiveTemplate(this.state.channel, template._id, template.name),
3822
- count: 1,
3823
- isUnarchive: true,
3824
- });
3825
- }}
3826
- className="popover-action popover-archive-action"
3827
- >
3828
- <CapIcon type="archive" size="l" />
3829
- {this.props.intl.formatMessage(messages.unarchiveButton)}
3830
- </span>
3831
- </div>}
3832
3658
  <div className="popover-action-container">
3833
3659
  <span onClick={() => this.toggleDeleteTemplateModal(template)} className="popover-action" style={{cursor: 'pointer', padding: '8px 0px'}}>{deleteOption}</span>
3834
3660
  </div>
@@ -4393,9 +4219,14 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4393
4219
  const isWechatEmbedded = !this.props.isFullMode && channel.toUpperCase() === WECHAT;
4394
4220
  const channelLowerCase = (channel || '').toLowerCase();
4395
4221
  const isTraiDltFeature = this.checkDLTfeatureEnable();
4396
-
4397
4222
  const createButton =
4398
- ( (channelLowerCase === WHATSAPP_LOWERCASE || channelLowerCase === RCS_LOWERCASE) && !this.props.isFullMode )
4223
+ (
4224
+ (
4225
+ channelLowerCase === WHATSAPP_LOWERCASE
4226
+ || channelLowerCase === RCS_LOWERCASE
4227
+ )
4228
+ && !this.props.isFullMode
4229
+ )
4399
4230
  ? (
4400
4231
  <CapLink
4401
4232
  onClick={this.openCreativesFullMode}
@@ -4421,22 +4252,23 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4421
4252
  if (([WHATSAPP_LOWERCASE, ZALO_LOWERCASE, RCS_LOWERCASE].includes(this.state?.channel?.toLocaleLowerCase()) && isEmpty(this.state?.hostName))) {
4422
4253
  isfilterContentVisisble = false;
4423
4254
  }
4424
- const _isArchivedMode = get(this.props, 'Templates.isArchivedMode', false);
4425
- const _renderSelectedIds = get(this.props, 'Templates.selectedTemplateIds', []);
4426
- const _renderSelectedIdsArray = _renderSelectedIds && typeof _renderSelectedIds.toJS === 'function' ? _renderSelectedIds.toJS() : (Array.isArray(_renderSelectedIds) ? _renderSelectedIds : []);
4427
- const _renderHasSelection = this.props.isFullMode && _renderSelectedIdsArray.length > 0;
4428
-
4429
- const filterContent = (( isfilterContentVisisble || [WECHAT, MOBILE_PUSH, INAPP].includes(this.state.channel.toUpperCase())) && <div className="action-container">
4430
- {isfilterContentVisisble && <CapInput.Search
4431
- className="search-text"
4432
- style={{width: '210px'}}
4433
- placeholder={_isArchivedMode ? this.props.intl.formatMessage(messages.searchArchivedTemplates) : this.props.intl.formatMessage(messages.searchText)}
4434
- value={this.state.searchText}
4435
- onChange={(e) => this.searchTemplate(e.target.value, this.state.channel)}
4436
- disabled={this.checkSearchDisabled()}
4437
- onClear={() => this.searchTemplate('', this.state.channel)}
4438
- onScroll={(e) => e.stopPropagation()}
4439
- />}
4255
+
4256
+ const useLocalTemplates = this.props.localTemplatesConfig?.useLocalTemplates;
4257
+ const builtFilterContent = ((isfilterContentVisisble || [WECHAT, MOBILE_PUSH, INAPP].includes(this.state.channel.toUpperCase())) && (
4258
+ <div className="action-container">
4259
+ <div className="action-container__toolbar-row">
4260
+ {isfilterContentVisisble ? (
4261
+ <CapInput.Search
4262
+ className="search-text"
4263
+ placeholder={this.props.intl.formatMessage(messages.searchText)}
4264
+ value={this.state.searchText}
4265
+ onChange={(e) => this.searchTemplate(e.target.value, this.state.channel)}
4266
+ onSearch={() => this.searchTemplate(this.state.searchText, this.state.channel)}
4267
+ onClear={() => this.searchTemplate('', this.state.channel)}
4268
+ onScroll={(e) => e.stopPropagation()}
4269
+ disabled={this.checkSearchDisabled()}
4270
+ />
4271
+ ) : null}
4440
4272
  {
4441
4273
  channel.toUpperCase() === WECHAT && <CapRadio.CapRadioGroup className="wechat-filters" defaultValue={wechatFilter} onChange={this.setWechatFilter}>
4442
4274
  <CapRadio.Button value={WECHAT_FILTERS.ALL}><CapLabel type="label2">
@@ -4582,46 +4414,27 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4582
4414
  </div>
4583
4415
  )
4584
4416
  }
4585
- <div className="template-listing-header-actions">
4586
- {!_isArchivedMode && !_renderHasSelection && (
4587
- this.state?.channel?.toLowerCase() === WHATSAPP_LOWERCASE && (isWhatsappCountExeeded) ? (
4588
- <CapTooltip title={whatsappCountExceedText}>
4589
- <div className="button-disabled-tooltip-wrapper">
4590
- {createButton}
4591
- </div>
4592
- </CapTooltip>
4593
- )
4594
- : isfilterContentVisisble && !isWechatEmbedded && !this.props.isDltFromRcs && createButton
4595
- )}
4596
- {/* More (⋯) menu: full mode only, not archived mode, not Zalo (no archive support), not when selection active */}
4597
- {!_isArchivedMode && !_renderHasSelection && this.props.isFullMode && this.props.location.query.type !== 'embedded' && channelLowerCase !== ZALO_LOWERCASE && (
4598
- <CapDropdown
4599
- trigger={['click']}
4600
- overlay={
4601
- <CapMenu>
4602
- <CapMenu.Item
4603
- key="archived"
4604
- onClick={() => {
4605
- this.props.actions.setArchivedMode(true);
4606
- this.setState({ searchText: '', page: 1 }, () => {
4607
- const params = { name: '', sortBy: this.state.sortBy, archiveStatus: 'archived' };
4608
- this.getAllTemplates({ params, resetPage: true }, true);
4609
- });
4610
- }}
4611
- >
4612
- <FormattedMessage {...messages.archivedTemplates} />
4613
- </CapMenu.Item>
4614
- </CapMenu>
4417
+ </div>
4418
+ <div>
4419
+ <div className="action-container__create-row">
4420
+ {
4421
+ this.state?.channel?.toLowerCase() === WHATSAPP_LOWERCASE && (isWhatsappCountExeeded) ? (
4422
+ <CapTooltip title={whatsappCountExceedText}>
4423
+ <div className="button-disabled-tooltip-wrapper">
4424
+ {createButton}
4425
+ </div>
4426
+ </CapTooltip>
4427
+ )
4428
+ : isfilterContentVisisble && !isWechatEmbedded && !this.props.isDltFromRcs && !this.props.isSmsFallbackFromRcs && createButton
4615
4429
  }
4616
- placement="bottomRight"
4617
- >
4618
- <CapButton type="flat" className="template-listing-more-btn">
4619
- <CapIcon type="more" />
4620
- </CapButton>
4621
- </CapDropdown>
4622
- )}
4430
+ </div>
4431
+ </div>
4623
4432
  </div>
4624
- </div>);
4433
+ ));
4434
+ const localTemplatesFilterContent = get(this.props, 'localTemplatesConfig.localTemplatesFilterContent', null);
4435
+ const filterContent = (useLocalTemplates && localTemplatesFilterContent) != null
4436
+ ? localTemplatesFilterContent
4437
+ : builtFilterContent;
4625
4438
  let htmlPreviewContent = "";
4626
4439
  if (this.state.channel.toLowerCase() === 'ebill') {
4627
4440
  htmlPreviewContent = this.state.previewTemplate && this.state.previewTemplate.versions && this.state.previewTemplate.versions.base && this.state.previewTemplate.versions.base['ebill-editor'];
@@ -4631,7 +4444,10 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4631
4444
 
4632
4445
 
4633
4446
  const creativesParams = this.getCreativesParams();
4634
- const templates = this.props.TemplatesList || [];
4447
+ const templates = useLocalTemplates
4448
+ ? (this.props.localTemplatesConfig?.localTemplates || [])
4449
+ : (this.props.TemplatesList || []);
4450
+ const isLoadingWhenLocal = useLocalTemplates && !!this.props.localTemplatesConfig?.localTemplatesLoading;
4635
4451
  const {route} = this.props;
4636
4452
  const loadingTipMap = {
4637
4453
  sendingFile: 'uploadingFile',
@@ -4646,9 +4462,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4646
4462
  (deleteRcsTemplateInProgress && 'deletingTemplate') ||
4647
4463
  (this.props.EmailCreate.duplicateTemplateInProgress && 'duplicatingTemplate');
4648
4464
 
4649
- const loadingTip = messages[loadingTipIntl] ? this.props.intl.formatMessage(messages[loadingTipIntl]) : this.props.intl.formatMessage(messages.gettingAllTemplates);
4465
+ const loadingTip = useLocalTemplates && this.props.localTemplatesConfig?.localTemplatesLoadingTip
4466
+ ? this.props.localTemplatesConfig.localTemplatesLoadingTip
4467
+ : (messages[loadingTipIntl] ? this.props.intl.formatMessage(messages[loadingTipIntl]) : this.props.intl.formatMessage(messages.gettingAllTemplates));
4650
4468
  const showNoTemplatesFoundZalo = this.state.channel.toUpperCase() === ZALO && isEmpty(this.state.searchedZaloTemplates) && this.state.searchingZaloTemplate;
4651
- const showNoTemplatesFoundOther = ![ZALO].includes(this.state.channel.toUpperCase()) && isEmpty(this.props.TemplatesList) && !this.props.Templates.getAllTemplatesInProgress && !isEmpty(this.state.searchText);
4469
+ const showNoTemplatesFoundOther = ![ZALO].includes(this.state.channel.toUpperCase()) && isEmpty(templates) && (useLocalTemplates ? !isLoadingWhenLocal : !this.props.Templates.getAllTemplatesInProgress) && (useLocalTemplates || !isEmpty(this.state.searchText));
4652
4470
  const showNoTemplatesFound = showNoTemplatesFoundZalo || showNoTemplatesFoundOther;
4653
4471
 
4654
4472
  return (
@@ -4669,24 +4487,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4669
4487
  }
4670
4488
  />
4671
4489
 
4672
- {/* Archived mode header with back arrow (full mode only) */}
4673
- {this.props.isFullMode && get(this.props, 'Templates.isArchivedMode', false) && (
4674
- <CapRow type="flex" align="middle" className="archived-mode-header">
4675
- <CapIcon
4676
- type="back"
4677
- className="archived-mode-back-icon"
4678
- onClick={() => {
4679
- this.props.actions.setArchivedMode(false);
4680
- this.setState({ searchText: '', page: 1 }, () => {
4681
- const params = { name: '', sortBy: this.state.sortBy, archiveStatus: 'active' };
4682
- this.getAllTemplates({ params, resetPage: true }, true);
4683
- });
4684
- }}
4685
- />
4686
- <CapHeading type="h3"><FormattedMessage {...messages.archivedTemplates} /></CapHeading>
4687
- </CapRow>
4688
- )}
4689
-
4690
4490
  {channel.toLowerCase() === WHATSAPP_LOWERCASE &&
4691
4491
  showWhatsappCountWarning ? (
4692
4492
  <CapAlert message={whatsappCountExceedText} type="info" />
@@ -4698,7 +4498,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4698
4498
  />
4699
4499
  ) : null}
4700
4500
 
4701
- {channel.toLowerCase() === RCS_LOWERCASE && !isFullMode ? (
4501
+ {channel.toLowerCase() === RCS_LOWERCASE && !isFullMode && this.state?.hostName !== HOST_INFOBIP ? (
4702
4502
  <CapInfoNote
4703
4503
  message={formatMessage(messages.rcsOnlyApprovedTemplates)}
4704
4504
  />
@@ -4711,22 +4511,22 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4711
4511
  ) : null}
4712
4512
  <CapRow>
4713
4513
  <Pagination
4714
- templateInProgress={
4715
- this.props.Templates.getAllTemplatesInProgress
4716
- }
4514
+ templateInProgress={useLocalTemplates ? isLoadingWhenLocal : this.props.Templates.getAllTemplatesInProgress}
4717
4515
  onPageChange={
4718
- templates.length ? this.onPaginationChange : () => {}
4516
+ templates.length
4517
+ ? (useLocalTemplates ? (this.props.localTemplatesConfig?.localTemplatesOnPageChange || (() => {})) : this.onPaginationChange)
4518
+ : () => {}
4719
4519
  }
4720
4520
  >
4721
4521
  {this.getTemplateDataForGrid({
4722
4522
  previewTemplateId: this.state.zaloPreviewItemId,
4723
- isLoading,
4724
- isInitialLoading,
4523
+ isLoading: useLocalTemplates ? isLoadingWhenLocal : isLoading,
4524
+ isInitialLoading: useLocalTemplates ? isLoadingWhenLocal && templates.length === 0 : isInitialLoading,
4725
4525
  loadingTip,
4726
4526
  channel: this.state.channel,
4727
4527
  templates: this.state.searchingZaloTemplate
4728
4528
  ? this.state.searchedZaloTemplates
4729
- : this.props.TemplatesList,
4529
+ : templates,
4730
4530
  filterContent,
4731
4531
  handlers: {
4732
4532
  handlePreviewClick: this.handlePreviewClick,
@@ -4909,6 +4709,15 @@ Templates.propTypes = {
4909
4709
  WebPush: PropTypes.object,
4910
4710
  smsRegister: PropTypes.any,
4911
4711
  isDltFromRcs: PropTypes.bool,
4712
+ isSmsFallbackFromRcs: PropTypes.bool,
4713
+ localTemplatesConfig: PropTypes.shape({
4714
+ useLocalTemplates: PropTypes.bool,
4715
+ localTemplates: PropTypes.arrayOf(PropTypes.object),
4716
+ localTemplatesLoading: PropTypes.bool,
4717
+ localTemplatesLoadingTip: PropTypes.string,
4718
+ localTemplatesFilterContent: PropTypes.node,
4719
+ localTemplatesOnPageChange: PropTypes.func,
4720
+ }),
4912
4721
  };
4913
4722
 
4914
4723
  const mapStateToProps = createStructuredSelector({