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

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 +0 -29
  2. package/package.json +1 -1
  3. package/services/api.js +20 -0
  4. package/services/tests/api.test.js +59 -13
  5. package/utils/commonUtils.js +1 -19
  6. package/v2Components/CapActionButton/constants.js +0 -7
  7. package/v2Components/CapActionButton/index.js +109 -167
  8. package/v2Components/CapActionButton/index.scss +6 -157
  9. package/v2Components/CapActionButton/messages.js +3 -19
  10. package/v2Components/CapActionButton/tests/index.test.js +17 -41
  11. package/v2Components/CapCustomSkeleton/index.js +1 -1
  12. package/v2Components/CapCustomSkeleton/tests/__snapshots__/index.test.js.snap +12 -12
  13. package/v2Components/CapTagList/index.js +0 -10
  14. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +49 -70
  15. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
  16. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -207
  17. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
  18. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +10 -85
  19. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
  20. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -79
  21. package/v2Components/CommonTestAndPreview/SendTestMessage.js +5 -10
  22. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +15 -160
  23. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +76 -341
  24. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +4 -133
  25. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +0 -11
  26. package/v2Components/CommonTestAndPreview/constants.js +2 -38
  27. package/v2Components/CommonTestAndPreview/index.js +186 -676
  28. package/v2Components/CommonTestAndPreview/messages.js +3 -49
  29. package/v2Components/CommonTestAndPreview/sagas.js +6 -15
  30. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +284 -308
  31. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +65 -231
  32. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +5 -118
  33. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -341
  34. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +1 -8
  35. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +13 -34
  36. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +283 -281
  37. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
  38. package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -132
  39. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
  40. package/v2Components/FormBuilder/index.js +10 -8
  41. package/v2Components/TemplatePreview/_templatePreview.scss +23 -33
  42. package/v2Components/TemplatePreview/index.js +28 -143
  43. package/v2Components/TemplatePreview/tests/index.test.js +0 -142
  44. package/v2Components/TestAndPreviewSlidebox/index.js +1 -13
  45. package/v2Components/TestAndPreviewSlidebox/sagas.js +4 -11
  46. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +1 -3
  47. package/v2Containers/Assets/images/archive_Empty_Illustration.svg +9 -0
  48. package/v2Containers/CreativesContainer/SlideBoxContent.js +4 -36
  49. package/v2Containers/CreativesContainer/SlideBoxFooter.js +4 -11
  50. package/v2Containers/CreativesContainer/SlideBoxHeader.js +4 -29
  51. package/v2Containers/CreativesContainer/constants.js +0 -9
  52. package/v2Containers/CreativesContainer/index.js +108 -300
  53. package/v2Containers/CreativesContainer/index.scss +1 -51
  54. package/v2Containers/CreativesContainer/messages.js +4 -0
  55. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -78
  56. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
  57. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
  58. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +98 -357
  59. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +18 -20
  60. package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
  61. package/v2Containers/Rcs/constants.js +8 -119
  62. package/v2Containers/Rcs/index.js +812 -2375
  63. package/v2Containers/Rcs/index.scss +6 -276
  64. package/v2Containers/Rcs/messages.js +3 -38
  65. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +70345 -98302
  66. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +5 -0
  67. package/v2Containers/Rcs/tests/index.test.js +121 -152
  68. package/v2Containers/Rcs/tests/mockData.js +0 -38
  69. package/v2Containers/Rcs/tests/utils.test.js +30 -646
  70. package/v2Containers/Rcs/utils.js +11 -478
  71. package/v2Containers/Sms/Create/index.js +40 -100
  72. package/v2Containers/SmsTrai/Create/index.js +4 -9
  73. package/v2Containers/SmsTrai/Edit/constants.js +0 -2
  74. package/v2Containers/SmsTrai/Edit/index.js +130 -636
  75. package/v2Containers/SmsTrai/Edit/messages.js +4 -14
  76. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2296 -4249
  77. package/v2Containers/SmsWrapper/index.js +8 -37
  78. package/v2Containers/TagList/index.js +0 -6
  79. package/v2Containers/Templates/ChannelTypeIllustration.js +23 -6
  80. package/v2Containers/Templates/_templates.scss +126 -181
  81. package/v2Containers/Templates/actions.js +36 -11
  82. package/v2Containers/Templates/constants.js +23 -2
  83. package/v2Containers/Templates/index.js +333 -142
  84. package/v2Containers/Templates/messages.js +68 -0
  85. package/v2Containers/Templates/reducer.js +68 -0
  86. package/v2Containers/Templates/sagas.js +98 -55
  87. package/v2Containers/Templates/selectors.js +12 -0
  88. package/v2Containers/Templates/tests/ChannelTypeIllustration.test.js +12 -0
  89. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1256 -1042
  90. package/v2Containers/Templates/tests/index.test.js +6 -0
  91. package/v2Containers/Templates/tests/reducer.test.js +178 -0
  92. package/v2Containers/Templates/tests/sagas.test.js +436 -200
  93. package/v2Containers/Templates/tests/selector.test.js +32 -0
  94. package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -72
  95. package/v2Containers/TemplatesV2/index.js +23 -86
  96. package/v2Containers/Whatsapp/index.js +20 -3
  97. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -578
  98. package/utils/rcsPayloadUtils.js +0 -92
  99. package/utils/templateVarUtils.js +0 -201
  100. package/utils/tests/templateVarUtils.test.js +0 -204
  101. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js.rej +0 -18
  102. package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
  103. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
  104. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -87
  105. package/v2Components/SmsFallback/constants.js +0 -73
  106. package/v2Components/SmsFallback/index.js +0 -955
  107. package/v2Components/SmsFallback/index.scss +0 -265
  108. package/v2Components/SmsFallback/messages.js +0 -78
  109. package/v2Components/SmsFallback/smsFallbackUtils.js +0 -118
  110. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
  111. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
  112. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
  113. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -197
  114. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -277
  115. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
  116. package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
  117. package/v2Components/TemplatePreview/constants.js +0 -2
  118. package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
  119. package/v2Components/VarSegmentMessageEditor/index.js +0 -125
  120. package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
  121. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -43
  122. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -67
  123. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
  124. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
  125. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
  126. package/v2Containers/Rcs/index.js.rej +0 -1336
  127. package/v2Containers/Rcs/index.scss.rej +0 -74
  128. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -225
  129. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap.rej +0 -128
  130. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -318
  131. package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
  132. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
  133. package/v2Containers/SmsTrai/Edit/index.scss +0 -121
  134. package/v2Containers/Templates/TemplatesActionBar.js +0 -101
  135. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
  136. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
  137. package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
  138. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +0 -131
@@ -50,9 +50,7 @@ import {
50
50
  CapStatus,
51
51
  CapColoredTag,
52
52
  CapSpin,
53
- CapCard,
54
- CapColumn,
55
- CapCarousel
53
+ CapCheckbox,
56
54
  } from "@capillarytech/cap-ui-library";
57
55
  import { makeSelectTemplates, makeSelectTemplatesResponse } from './selectors';
58
56
  import { makeSelectCreate as makeSelectCreateSms } from '../Sms/Create/selectors';
@@ -116,23 +114,15 @@ import {
116
114
  STATUS as WHATSAPP_STATUS,
117
115
  WHATSAPP_STATUSES,
118
116
  HOST_GUPSHUP,
119
- HOST_HAPTIC,
120
117
  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,
127
118
  IMAGE,
128
119
  VIDEO,
129
- GIF,
130
120
  } from '../Whatsapp/constants';
131
- import { INAPP_LAYOUT_DETAILS, INAPP_MESSAGE_LAYOUT_TYPES, INAPP_MEDIA_TYPES, BIG_HTML, ANDROID, IOS } from '../InApp/constants';
121
+ import { INAPP_LAYOUT_DETAILS, INAPP_MESSAGE_LAYOUT_TYPES } from '../InApp/constants';
132
122
  import { ZALO_STATUS_OPTIONS, ZALO_STATUSES } from '../Zalo/constants';
133
123
  import { getWhatsappContent, getWhatsappStatus, getWhatsappCategory, getWhatsappCta, getWhatsappQuickReply, getWhatsappAutoFill, getWhatsappCarouselButtonView } from '../Whatsapp/utils';
134
124
  import { getRCSContent } from '../Rcs/utils';
135
- import { RCS_STATUSES, HOST_INFOBIP } from '../Rcs/constants';
125
+ import {RCS_STATUSES} from '../Rcs/constants';
136
126
  import zaloMessages from '../Zalo/messages';
137
127
  import rcsMessages from '../Rcs/messages';
138
128
  import inAppMessages from '../InApp/messages';
@@ -145,7 +135,7 @@ import {CREATIVE} from '../Facebook/constants';
145
135
  import videoPlay from '../../assets/videoPlay.svg';
146
136
  import whatsappImageEmptyPreview from '../../v2Components/TemplatePreview/assets/images/empty_image_preview.svg';
147
137
  import whatsappVideoEmptyPreview from '../../v2Components/TemplatePreview/assets/images/empty_video_preview.svg';
148
- import { CAP_SPACE_16 } from '@capillarytech/cap-ui-library/styled/variables';
138
+ import { CAP_SPACE_16, CAP_G08, CAP_G05, CAP_SPACE_08, CAP_SPACE_12 } from '@capillarytech/cap-ui-library/styled/variables';
149
139
  import { GA } from '@capillarytech/cap-ui-utils';
150
140
  import { MAPP_SDK } from '../InApp/constants';
151
141
  import injectReducer from '../../utils/injectReducer';
@@ -154,7 +144,6 @@ import { compose } from 'redux';
154
144
  import { v2TemplateSaga } from './sagas';
155
145
  import injectSaga from '../../utils/injectSaga';
156
146
  import { DAEMON } from '@capillarytech/vulcan-react-sdk/utils/sagaInjectorTypes';
157
- import { Rcs } from '../Rcs';
158
147
  import { makeSelectRcs } from '../Rcs/selectors';
159
148
  import { getRcsStatusType } from '../Rcs/utils';
160
149
  import { makeSelectWebPush } from '../WebPush/selectors';
@@ -437,6 +426,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
437
426
  channel = '';
438
427
  }
439
428
  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();
440
432
  // Clear templates when entering account selection mode to prevent showing old channel templates
441
433
  if (activeMode === ACCOUNT_SELECTION_MODE) {
442
434
  this.props.actions.resetTemplate();
@@ -468,13 +460,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
468
460
  if (this.props.location.query.type === 'embedded') {
469
461
  this.props.actions.resetAccount();
470
462
  }
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())) {
463
+ if (['line', VIBER_CHANNEL, FACEBOOK_CHANNEL, 'sms', 'email', 'ebill'].includes((this.state.channel || '').toLowerCase())) {
478
464
  const queryParams = {
479
465
  // name: this.state.searchText,
480
466
  // sortBy: this.state.sortBy,
@@ -987,16 +973,8 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
987
973
 
988
974
  componentWillUnmount() {
989
975
  window.removeEventListener("message", this.handleFrameTasks);
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
- }
976
+ this.props.actions.resetTemplateStoreData();
977
+ this.props.globalActions.clearMetaEntities();
1000
978
  // Clear any pending timeouts to prevent memory leaks
1001
979
  if (this._clearEditTimeout) {
1002
980
  clearTimeout(this._clearEditTimeout);
@@ -1609,6 +1587,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1609
1587
  let queryParams = params || {};
1610
1588
  let page = this.state.page;
1611
1589
  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
+ }
1612
1595
  if (activeMode === ACCOUNT_SELECTION_MODE) {
1613
1596
  this.setTemplatesMode();
1614
1597
  }
@@ -1781,20 +1764,12 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1781
1764
  }
1782
1765
 
1783
1766
  filterRcsTemplates = (templates) => {
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
- );
1791
- }
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
- );
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);
1796
1771
  }
1797
- return nextTemplates;
1772
+ return templates;
1798
1773
  }
1799
1774
 
1800
1775
  filterZaloTemplates = (templates) => {
@@ -1883,6 +1858,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1883
1858
  const currentChannel = channel.toUpperCase();
1884
1859
  const {channel: stateChannel} = this.state;
1885
1860
  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;
1886
1864
  let filteredTemplates = templates;
1887
1865
  let isTraiDltFeature = false;
1888
1866
  switch (currentChannel) {
@@ -1922,10 +1900,26 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1922
1900
  const iosBodyType = get(template, 'versions.base.content.IOS.bodyType');
1923
1901
  const inappBodyType = androidBodyType || iosBodyType;
1924
1902
  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));
1925
1911
  const templateData = {
1926
1912
  key: `${currentChannel}-card-${template?.name}`,
1927
1913
  title: (
1928
- <span title={template?.name}>
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
+ )}
1929
1923
  {template?.name}
1930
1924
  {currentChannel === INAPP && (
1931
1925
  <CapRow>
@@ -1969,7 +1963,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1969
1963
  style={{ marginRight: "16px" }}
1970
1964
  type="eye"
1971
1965
  onClick={() => {
1972
- if (!this.props.isFullMode || this.props.isDltFromRcs || this.props.isSmsFallbackFromRcs) {
1966
+ if (!this.props.isFullMode || this.props.isDltFromRcs) {
1973
1967
  if (!get(template, "versions.base.content.zalo.previewUrl", "")) {
1974
1968
  this.setState({ zaloPreviewItemId: template?._id });
1975
1969
  }
@@ -2046,7 +2040,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2046
2040
  {![WECHAT, WHATSAPP, ZALO].includes(
2047
2041
  this.state.channel.toUpperCase()
2048
2042
  ) &&
2049
- !isTraiDltFeature && (
2043
+ !isTraiDltFeature && !template.isArchived && (
2050
2044
  <CapMenu.Item
2051
2045
  className={`duplicate-${channelLowerCase}`}
2052
2046
  onClick={() => this.duplicateTemplate(template)}
@@ -2054,6 +2048,42 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2054
2048
  <FormattedMessage {...messages.duplicateButton} />
2055
2049
  </CapMenu.Item>
2056
2050
  )}
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
+ })()}
2057
2087
  {/* Delete/Unmap menu item */}
2058
2088
  {(!(
2059
2089
  this.state.channel.toUpperCase() === WHATSAPP &&
@@ -2334,7 +2364,15 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2334
2364
  case WHATSAPP: {
2335
2365
  const { whatsappImageSrc = '', templateMsg, docPreview, whatsappVideoPreviewImg = '', templateHeaderPreview, templateFooterPreview, carouselData = [] } = getWhatsappContent(template);
2336
2366
  templateData.title = (
2337
- <CapRow>
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
+ )}
2338
2376
  <CapLabel className="whatsapp-rcs-template-name">{template?.name}</CapLabel>
2339
2377
  <WhatsappStatusContainer template={template} />
2340
2378
  </CapRow>
@@ -2427,16 +2465,23 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2427
2465
  const name = get(template, "name", "");
2428
2466
  const statusDisplay=getRcsStatusType(status);
2429
2467
  templateData.title = (
2430
- <CapRow>
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
+ )}
2431
2477
  <CapLabel className="whatsapp-rcs-template-name">{name}</CapLabel>
2432
- {this.state.hostName !== HOST_INFOBIP && <CapRow type="flex" align="middle" className="rcs-status-container zalo-status-color">
2478
+ <CapRow type="flex" align="middle" className="rcs-status-container zalo-status-color">
2433
2479
  <CapStatus
2434
2480
  type={statusDisplay}
2435
- text={statusDisplay && this.props.intl.formatMessage(rcsMessages?.[`${statusDisplay}_STATUS`])}
2436
- labelType="label3"
2437
- />
2438
- </CapRow>
2439
- }
2481
+ text={statusDisplay && this.props.intl.formatMessage(rcsMessages?.[`${statusDisplay}_STATUS`])}
2482
+ labelType="label3"
2483
+ />
2484
+ </CapRow>
2440
2485
  </CapRow>
2441
2486
  );
2442
2487
 
@@ -2552,7 +2597,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2552
2597
 
2553
2598
  //no templates available
2554
2599
  const showIllustrationForChannel = (forChannel) => {
2555
- return forChannel === channelLowerCase && isEmpty(templates) && isEmpty(this.state.searchText) && !isLoading;
2600
+ return forChannel === channelLowerCase && isEmpty(templates) && isEmpty(this.state.searchText) && !isLoading && !get(this.props, 'Templates.isArchivedMode', false);
2556
2601
  }
2557
2602
  //when filters applied not matching templates
2558
2603
  const filteredEmptyAndFullModeAs = (fullModeValue) => {
@@ -2578,9 +2623,49 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2578
2623
 
2579
2624
  const noLoaderAndSearchText = isEmpty(this.state.searchText) && !isLoading;
2580
2625
 
2581
- return (<div>
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>
2582
2633
  {[WECHAT, MOBILE_PUSH, WEBPUSH, INAPP, WHATSAPP, ZALO, RCS].includes(currentChannel) && this.showAccountName()}
2583
- {filterContent}
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>
2584
2669
  {[WHATSAPP, ZALO, INAPP,RCS].includes(currentChannel) && this.selectedFilters()}
2585
2670
  {<div>
2586
2671
  {!isEmpty(filteredTemplates) || !isEmpty(this.state.searchText) || !isEmpty(this.props.Templates.templateError) ? (
@@ -2593,7 +2678,7 @@ return (<div>
2593
2678
  fbType={"list"}
2594
2679
  />
2595
2680
  </div>)
2596
- : [SMS_LOWERCASE, EMAIL_LOWERCASE].includes(this.state.channel.toLowerCase()) && !isLoading && this.getSmsEmailIllustration()
2681
+ : [SMS_LOWERCASE, EMAIL_LOWERCASE].includes(this.state.channel.toLowerCase()) && !isLoading && !get(this.props, 'Templates.isArchivedMode', false) && this.getSmsEmailIllustration()
2597
2682
  }
2598
2683
 
2599
2684
  {(this.state.selectedAccount === '' && isEmpty(this.props.Templates.selectedWeChatAccount)) && ([WECHAT_LOWERCASE, MOBILE_PUSH_LOWERCASE, INAPP_LOWERCASE].includes(this.state.channel.toLowerCase())) &&
@@ -2617,7 +2702,7 @@ return (<div>
2617
2702
  </div>
2618
2703
  )
2619
2704
  }
2620
- {(showWhatsappIllustration || showZaloIllustration) && (
2705
+ {(showWhatsappIllustration || showZaloIllustration) && !get(this.props, 'Templates.isArchivedMode', false) && (
2621
2706
  noLoaderAndSearchText &&
2622
2707
  <div style={this.isFullMode() ? { height: "calc(100vh - 325px)", overflow: 'auto' } : {}}>
2623
2708
  {noWhatsappZaloTemplates && <ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={currentChannel} hostName={this.state?.hostName}/>}
@@ -2658,6 +2743,16 @@ return (<div>
2658
2743
  <ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={currentChannel} hostName={this.state?.hostName}/>
2659
2744
  </div>
2660
2745
  }
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
+ )}
2661
2756
  {<CapCustomSkeleton loader={isInitialLoading && (isLoading || getAllTemplatesInProgress)} />}
2662
2757
  {<CapPageSpinner spinning={!isInitialLoading && (isLoading || getAllTemplatesInProgress)} />}
2663
2758
  </div>
@@ -2960,7 +3055,6 @@ return (<div>
2960
3055
  let routeParams = {};
2961
3056
  const {fbAdManager} = this.props;
2962
3057
  const isLibraryMode = this.isEnabledInLibraryModule("callCreateFromProps");
2963
-
2964
3058
  if (!isLibraryMode) {
2965
3059
  timeTracker.startTimer(CHANNEL_CREATE_TRACK_MAPPING[channel]);
2966
3060
  }
@@ -3302,17 +3396,14 @@ return (<div>
3302
3396
  };
3303
3397
 
3304
3398
  handleEditClick(e, template, modeType, path, options) {
3399
+ if (template && template.isArchived) {
3400
+ CapNotification.error({ message: this.props.intl.formatMessage(messages.cannotEditArchivedTemplate) });
3401
+ return;
3402
+ }
3305
3403
  if (modeType && modeType !== undefined) {
3306
3404
  this.setState({modeType});
3307
3405
  }
3308
3406
  const { _id: id } = template;
3309
- const {
3310
- localTemplatesConfig,
3311
- fbAdManager,
3312
- isDltFromRcs,
3313
- isSmsFallbackFromRcs,
3314
- onSelectTemplate,
3315
- } = this.props;
3316
3407
  const type = this.props.location.query.type;
3317
3408
  const module = this.props.location.query.module;
3318
3409
  const isLanguageSupport = (this.props.location.query.isLanguageSupport) ? this.props.location.query.isLanguageSupport : false;
@@ -3398,12 +3489,10 @@ return (<div>
3398
3489
  }
3399
3490
  if (this.isEnabledInLibraryModule("callSelectFromProps")) {
3400
3491
  let data = id;
3401
- if (localTemplatesConfig?.useLocalTemplates) {
3402
- data = template;
3403
- } else if (fbAdManager || isDltFromRcs || isSmsFallbackFromRcs) {
3492
+ if (this.props.fbAdManager || this.props.isDltFromRcs) {
3404
3493
  data = this.selectTemplate(id);
3405
3494
  }
3406
- onSelectTemplate(data, fbAdManager);
3495
+ this.props.onSelectTemplate(data, this.props.fbAdManager);
3407
3496
  } else {
3408
3497
  timeTracker.startTimer(CHANNEL_EDIT_TRACK_MAPPING[this.state.channel.toLowerCase()]);
3409
3498
  if (this.state.channel.toLowerCase() === 'ebill') {
@@ -3492,6 +3581,33 @@ return (<div>
3492
3581
  this.setState({showModal: false});
3493
3582
  }
3494
3583
 
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
+
3495
3611
  populateTemplatesList = (data, blankTemplateRequired, layoutSelection) => {
3496
3612
  if (!data) {
3497
3613
  return [];
@@ -3641,20 +3757,78 @@ return (<div>
3641
3757
  deleteOption = this.props.intl.formatMessage(messages.unMapButton);
3642
3758
  }
3643
3759
  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
+
3644
3786
  temp.footer = (
3645
3787
  <div className="footer-container">
3646
3788
  <div className="card-title">
3647
3789
  <span className="template-name" style={{ fontWeight: `${this.state.channel.toLowerCase() === 'wechat' ? '400' : '600'}` }}>
3648
3790
  { 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>}
3649
3791
  {template?.name}
3792
+ {template.isArchived && <CapColoredTag tagColor={CAP_G08} tagTextColor={CAP_G05} className="archived-tag">{this.props.intl.formatMessage(messages.archivedTag)}</CapColoredTag>}
3650
3793
  </span>
3651
3794
  {this.props.location.query.type !== 'embedded' && <CapPopover
3652
3795
  trigger="click"
3653
3796
  content={
3654
3797
  <div className="popover-content">
3655
- {this.state.channel !== 'wechat' && <div className="popover-action-container">
3798
+ {this.state.channel !== 'wechat' && !template.isArchived && <div className="popover-action-container">
3656
3799
  <span onClick={() => this.duplicateTemplate(template)} className="popover-action" style={{cursor: 'pointer', padding: '8px 0px'}}>{this.props.intl.formatMessage(messages.duplicateButton)}</span>
3657
3800
  </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>}
3658
3832
  <div className="popover-action-container">
3659
3833
  <span onClick={() => this.toggleDeleteTemplateModal(template)} className="popover-action" style={{cursor: 'pointer', padding: '8px 0px'}}>{deleteOption}</span>
3660
3834
  </div>
@@ -4219,14 +4393,9 @@ return (<div>
4219
4393
  const isWechatEmbedded = !this.props.isFullMode && channel.toUpperCase() === WECHAT;
4220
4394
  const channelLowerCase = (channel || '').toLowerCase();
4221
4395
  const isTraiDltFeature = this.checkDLTfeatureEnable();
4396
+
4222
4397
  const createButton =
4223
- (
4224
- (
4225
- channelLowerCase === WHATSAPP_LOWERCASE
4226
- || channelLowerCase === RCS_LOWERCASE
4227
- )
4228
- && !this.props.isFullMode
4229
- )
4398
+ ( (channelLowerCase === WHATSAPP_LOWERCASE || channelLowerCase === RCS_LOWERCASE) && !this.props.isFullMode )
4230
4399
  ? (
4231
4400
  <CapLink
4232
4401
  onClick={this.openCreativesFullMode}
@@ -4252,23 +4421,22 @@ return (<div>
4252
4421
  if (([WHATSAPP_LOWERCASE, ZALO_LOWERCASE, RCS_LOWERCASE].includes(this.state?.channel?.toLocaleLowerCase()) && isEmpty(this.state?.hostName))) {
4253
4422
  isfilterContentVisisble = false;
4254
4423
  }
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}
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
+ />}
4272
4440
  {
4273
4441
  channel.toUpperCase() === WECHAT && <CapRadio.CapRadioGroup className="wechat-filters" defaultValue={wechatFilter} onChange={this.setWechatFilter}>
4274
4442
  <CapRadio.Button value={WECHAT_FILTERS.ALL}><CapLabel type="label2">
@@ -4414,27 +4582,46 @@ return (<div>
4414
4582
  </div>
4415
4583
  )
4416
4584
  }
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
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>
4429
4615
  }
4430
- </div>
4431
- </div>
4616
+ placement="bottomRight"
4617
+ >
4618
+ <CapButton type="flat" className="template-listing-more-btn">
4619
+ <CapIcon type="more" />
4620
+ </CapButton>
4621
+ </CapDropdown>
4622
+ )}
4432
4623
  </div>
4433
- ));
4434
- const localTemplatesFilterContent = get(this.props, 'localTemplatesConfig.localTemplatesFilterContent', null);
4435
- const filterContent = (useLocalTemplates && localTemplatesFilterContent) != null
4436
- ? localTemplatesFilterContent
4437
- : builtFilterContent;
4624
+ </div>);
4438
4625
  let htmlPreviewContent = "";
4439
4626
  if (this.state.channel.toLowerCase() === 'ebill') {
4440
4627
  htmlPreviewContent = this.state.previewTemplate && this.state.previewTemplate.versions && this.state.previewTemplate.versions.base && this.state.previewTemplate.versions.base['ebill-editor'];
@@ -4444,10 +4631,7 @@ return (<div>
4444
4631
 
4445
4632
 
4446
4633
  const creativesParams = this.getCreativesParams();
4447
- const templates = useLocalTemplates
4448
- ? (this.props.localTemplatesConfig?.localTemplates || [])
4449
- : (this.props.TemplatesList || []);
4450
- const isLoadingWhenLocal = useLocalTemplates && !!this.props.localTemplatesConfig?.localTemplatesLoading;
4634
+ const templates = this.props.TemplatesList || [];
4451
4635
  const {route} = this.props;
4452
4636
  const loadingTipMap = {
4453
4637
  sendingFile: 'uploadingFile',
@@ -4462,11 +4646,9 @@ return (<div>
4462
4646
  (deleteRcsTemplateInProgress && 'deletingTemplate') ||
4463
4647
  (this.props.EmailCreate.duplicateTemplateInProgress && 'duplicatingTemplate');
4464
4648
 
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));
4649
+ const loadingTip = messages[loadingTipIntl] ? this.props.intl.formatMessage(messages[loadingTipIntl]) : this.props.intl.formatMessage(messages.gettingAllTemplates);
4468
4650
  const showNoTemplatesFoundZalo = this.state.channel.toUpperCase() === ZALO && isEmpty(this.state.searchedZaloTemplates) && this.state.searchingZaloTemplate;
4469
- const showNoTemplatesFoundOther = ![ZALO].includes(this.state.channel.toUpperCase()) && isEmpty(templates) && (useLocalTemplates ? !isLoadingWhenLocal : !this.props.Templates.getAllTemplatesInProgress) && (useLocalTemplates || !isEmpty(this.state.searchText));
4651
+ const showNoTemplatesFoundOther = ![ZALO].includes(this.state.channel.toUpperCase()) && isEmpty(this.props.TemplatesList) && !this.props.Templates.getAllTemplatesInProgress && !isEmpty(this.state.searchText);
4470
4652
  const showNoTemplatesFound = showNoTemplatesFoundZalo || showNoTemplatesFoundOther;
4471
4653
 
4472
4654
  return (
@@ -4487,6 +4669,24 @@ return (<div>
4487
4669
  }
4488
4670
  />
4489
4671
 
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
+
4490
4690
  {channel.toLowerCase() === WHATSAPP_LOWERCASE &&
4491
4691
  showWhatsappCountWarning ? (
4492
4692
  <CapAlert message={whatsappCountExceedText} type="info" />
@@ -4498,7 +4698,7 @@ return (<div>
4498
4698
  />
4499
4699
  ) : null}
4500
4700
 
4501
- {channel.toLowerCase() === RCS_LOWERCASE && !isFullMode && this.state?.hostName !== HOST_INFOBIP ? (
4701
+ {channel.toLowerCase() === RCS_LOWERCASE && !isFullMode ? (
4502
4702
  <CapInfoNote
4503
4703
  message={formatMessage(messages.rcsOnlyApprovedTemplates)}
4504
4704
  />
@@ -4511,22 +4711,22 @@ return (<div>
4511
4711
  ) : null}
4512
4712
  <CapRow>
4513
4713
  <Pagination
4514
- templateInProgress={useLocalTemplates ? isLoadingWhenLocal : this.props.Templates.getAllTemplatesInProgress}
4714
+ templateInProgress={
4715
+ this.props.Templates.getAllTemplatesInProgress
4716
+ }
4515
4717
  onPageChange={
4516
- templates.length
4517
- ? (useLocalTemplates ? (this.props.localTemplatesConfig?.localTemplatesOnPageChange || (() => {})) : this.onPaginationChange)
4518
- : () => {}
4718
+ templates.length ? this.onPaginationChange : () => {}
4519
4719
  }
4520
4720
  >
4521
4721
  {this.getTemplateDataForGrid({
4522
4722
  previewTemplateId: this.state.zaloPreviewItemId,
4523
- isLoading: useLocalTemplates ? isLoadingWhenLocal : isLoading,
4524
- isInitialLoading: useLocalTemplates ? isLoadingWhenLocal && templates.length === 0 : isInitialLoading,
4723
+ isLoading,
4724
+ isInitialLoading,
4525
4725
  loadingTip,
4526
4726
  channel: this.state.channel,
4527
4727
  templates: this.state.searchingZaloTemplate
4528
4728
  ? this.state.searchedZaloTemplates
4529
- : templates,
4729
+ : this.props.TemplatesList,
4530
4730
  filterContent,
4531
4731
  handlers: {
4532
4732
  handlePreviewClick: this.handlePreviewClick,
@@ -4709,15 +4909,6 @@ Templates.propTypes = {
4709
4909
  WebPush: PropTypes.object,
4710
4910
  smsRegister: PropTypes.any,
4711
4911
  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
- }),
4721
4912
  };
4722
4913
 
4723
4914
  const mapStateToProps = createStructuredSelector({