@capillarytech/creatives-library 8.0.340-beta.0.5 → 8.0.340-beta.0.8

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 (62) hide show
  1. package/constants/unified.js +1 -0
  2. package/package.json +1 -1
  3. package/services/api.js +20 -0
  4. package/services/tests/api.test.js +59 -0
  5. package/utils/common.js +6 -0
  6. package/utils/test-utils.js +2 -2
  7. package/utils/tests/v2Common.test.js +46 -1
  8. package/utils/v2common.js +18 -0
  9. package/v2Components/CapTagList/index.js +5 -6
  10. package/v2Components/CommonTestAndPreview/UnifiedPreview/WhatsAppPreviewContent.js +18 -6
  11. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +27 -0
  12. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/WhatsAppPreviewContent.test.js +48 -0
  13. package/v2Components/TemplatePreview/_templatePreview.scss +21 -0
  14. package/v2Components/TemplatePreview/index.js +18 -6
  15. package/v2Components/TemplatePreview/tests/__snapshots__/index.test.js.snap +1 -0
  16. package/v2Containers/Assets/images/archive_Empty_Illustration.svg +9 -0
  17. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +28 -20
  18. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +24 -16
  19. package/v2Containers/CreativesContainer/SlideBoxContent.js +16 -5
  20. package/v2Containers/CreativesContainer/SlideBoxFooter.js +3 -1
  21. package/v2Containers/CreativesContainer/index.js +14 -1
  22. package/v2Containers/CreativesContainer/messages.js +4 -0
  23. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +2 -4
  24. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +3 -0
  25. package/v2Containers/Email/reducer.js +12 -3
  26. package/v2Containers/Email/sagas.js +9 -4
  27. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +4 -0
  28. package/v2Containers/Email/tests/reducer.test.js +47 -0
  29. package/v2Containers/Email/tests/sagas.test.js +146 -6
  30. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +8 -1
  31. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +1 -0
  32. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +7 -0
  33. package/v2Containers/Sms/Create/index.js +3 -0
  34. package/v2Containers/Templates/ChannelTypeIllustration.js +23 -6
  35. package/v2Containers/Templates/_templates.scss +155 -24
  36. package/v2Containers/Templates/actions.js +44 -0
  37. package/v2Containers/Templates/constants.js +31 -0
  38. package/v2Containers/Templates/index.js +400 -59
  39. package/v2Containers/Templates/messages.js +96 -0
  40. package/v2Containers/Templates/reducer.js +84 -1
  41. package/v2Containers/Templates/sagas.js +64 -0
  42. package/v2Containers/Templates/selectors.js +12 -0
  43. package/v2Containers/Templates/tests/ChannelTypeIllustration.test.js +12 -0
  44. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1166 -1112
  45. package/v2Containers/Templates/tests/index.test.js +6 -0
  46. package/v2Containers/Templates/tests/reducer.test.js +178 -0
  47. package/v2Containers/Templates/tests/sagas.test.js +390 -8
  48. package/v2Containers/Templates/tests/selector.test.js +32 -0
  49. package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -1
  50. package/v2Containers/Viber/constants.js +8 -0
  51. package/v2Containers/Viber/index.js +5 -0
  52. package/v2Containers/Viber/messages.js +4 -0
  53. package/v2Containers/Viber/reducer.js +26 -3
  54. package/v2Containers/Viber/sagas.js +50 -8
  55. package/v2Containers/Viber/tests/index.test.js +80 -0
  56. package/v2Containers/Viber/tests/reducer.test.js +297 -0
  57. package/v2Containers/Viber/tests/saga.test.js +412 -40
  58. package/v2Containers/WeChat/Wrapper/index.js +0 -1
  59. package/v2Containers/Whatsapp/constants.js +8 -0
  60. package/v2Containers/Whatsapp/index.js +145 -5
  61. package/v2Containers/Whatsapp/index.scss +12 -0
  62. package/v2Containers/Whatsapp/messages.js +16 -0
@@ -50,9 +50,8 @@ import {
50
50
  CapStatus,
51
51
  CapColoredTag,
52
52
  CapSpin,
53
- CapCard,
53
+ CapCheckbox,
54
54
  CapColumn,
55
- CapCarousel
56
55
  } from "@capillarytech/cap-ui-library";
57
56
  import { makeSelectTemplates, makeSelectTemplatesResponse } from './selectors';
58
57
  import { makeSelectCreate as makeSelectCreateSms } from '../Sms/Create/selectors';
@@ -83,7 +82,7 @@ import * as webpushActions from '../WebPush/actions';
83
82
  import * as globalActions from '../Cap/actions';
84
83
  import { makeSelectAuthenticated } from '../Cap/selectors';
85
84
  import { UserIsAuthenticated } from '../../utils/authWrapper';
86
- import { getObjFromQueryParams } from '../../utils/v2common';
85
+ import { getObjFromQueryParams, buildTemplateNameDescription } from '../../utils/v2common';
87
86
  import messages from './messages';
88
87
  import {checkUnicode} from '../../utils/smsCharCountV2';
89
88
  import { containsBase64Images } from '../../utils/content';
@@ -107,8 +106,8 @@ import {
107
106
  FACEBOOK as FACEBOOK_CHANNEL,
108
107
  CREATE,
109
108
  } from '../App/constants';
110
- import {MAX_WHATSAPP_TEMPLATES, WARNING_WHATSAPP_TEMPLATES , ACCOUNT_MAPPING_ON_CHANNEL, noFilteredWhatsappZaloTemplatesTitle, noFilteredWhatsappZaloTemplatesDesc, noApprovedWhatsappZaloTemplatesTitle, noApprovedWhatsappTemplatesDesc, zaloDescIllustration, noApprovedRcsTemplatesTitle, noApprovedRcsTemplatesDesc} from './constants';
111
- import { COPY_OF } from '../../constants/unified';
109
+ import {MAX_WHATSAPP_TEMPLATES, WARNING_WHATSAPP_TEMPLATES , ACCOUNT_MAPPING_ON_CHANNEL, noFilteredWhatsappZaloTemplatesTitle, noFilteredWhatsappZaloTemplatesDesc, noApprovedWhatsappZaloTemplatesTitle, noApprovedWhatsappTemplatesDesc, zaloDescIllustration, noApprovedRcsTemplatesTitle, noApprovedRcsTemplatesDesc, ARCHIVE_STATUS_ACTIVE, ARCHIVE_STATUS_ARCHIVED, ARCHIVE_REFRESH_TYPE_ARCHIVE, ARCHIVE_REFRESH_TYPE_UNARCHIVE} from './constants';
110
+ import { COPY_OF, EMBEDDED } from '../../constants/unified';
112
111
  import {
113
112
  STATUS_OPTIONS,
114
113
  CATEGORY,
@@ -116,19 +115,11 @@ import {
116
115
  STATUS as WHATSAPP_STATUS,
117
116
  WHATSAPP_STATUSES,
118
117
  HOST_GUPSHUP,
119
- HOST_HAPTIC,
120
118
  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
119
  IMAGE,
128
120
  VIDEO,
129
- GIF,
130
121
  } from '../Whatsapp/constants';
131
- import { INAPP_LAYOUT_DETAILS, INAPP_MESSAGE_LAYOUT_TYPES, INAPP_MEDIA_TYPES, BIG_HTML, ANDROID, IOS } from '../InApp/constants';
122
+ import { INAPP_LAYOUT_DETAILS, INAPP_MESSAGE_LAYOUT_TYPES } from '../InApp/constants';
132
123
  import { ZALO_STATUS_OPTIONS, ZALO_STATUSES } from '../Zalo/constants';
133
124
  import { getWhatsappContent, getWhatsappStatus, getWhatsappCategory, getWhatsappCta, getWhatsappQuickReply, getWhatsappAutoFill, getWhatsappCarouselButtonView } from '../Whatsapp/utils';
134
125
  import { getRCSContent } from '../Rcs/utils';
@@ -145,7 +136,7 @@ import {CREATIVE} from '../Facebook/constants';
145
136
  import videoPlay from '../../assets/videoPlay.svg';
146
137
  import whatsappImageEmptyPreview from '../../v2Components/TemplatePreview/assets/images/empty_image_preview.svg';
147
138
  import whatsappVideoEmptyPreview from '../../v2Components/TemplatePreview/assets/images/empty_video_preview.svg';
148
- import { CAP_SPACE_16 } from '@capillarytech/cap-ui-library/styled/variables';
139
+ import { CAP_SPACE_16, CAP_G08, CAP_G05, CAP_SPACE_08, CAP_SPACE_12 } from '@capillarytech/cap-ui-library/styled/variables';
149
140
  import { GA } from '@capillarytech/cap-ui-utils';
150
141
  import { MAPP_SDK } from '../InApp/constants';
151
142
  import injectReducer from '../../utils/injectReducer';
@@ -154,7 +145,6 @@ import { compose } from 'redux';
154
145
  import { v2TemplateSaga } from './sagas';
155
146
  import injectSaga from '../../utils/injectSaga';
156
147
  import { DAEMON } from '@capillarytech/vulcan-react-sdk/utils/sagaInjectorTypes';
157
- import { Rcs } from '../Rcs';
158
148
  import { makeSelectRcs } from '../Rcs/selectors';
159
149
  import { getRcsStatusType } from '../Rcs/utils';
160
150
  import { makeSelectWebPush } from '../WebPush/selectors';
@@ -265,6 +255,8 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
265
255
  templatesCount: 0,
266
256
  showEdmEmailTemplates: false,
267
257
  showModal: false,
258
+ showArchiveModal: false,
259
+ archiveModalContext: null,
268
260
  actionTemplate: {},
269
261
  showHtmlPreview: false,
270
262
  device: 'desktop',
@@ -437,6 +429,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
437
429
  channel = '';
438
430
  }
439
431
  this.setState({ channel, activeMode });
432
+ // Always reset archive mode and selection when mounting a new channel
433
+ this.props.actions.setArchivedMode(false);
434
+ this.props.actions.clearTemplateSelection();
440
435
  // Clear templates when entering account selection mode to prevent showing old channel templates
441
436
  if (activeMode === ACCOUNT_SELECTION_MODE) {
442
437
  this.props.actions.resetTemplate();
@@ -827,6 +822,39 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
827
822
  this.getAllTemplates({params, resetPage: true});
828
823
  }
829
824
 
825
+ // Archive / Unarchive / Bulk Archive / Bulk Unarchive — detect completion and refresh listing
826
+ const wasArchiving = this.props.Templates.archiveInProgress;
827
+ const isArchiveDone = !nextProps.Templates.archiveInProgress && wasArchiving;
828
+ if (isArchiveDone && !nextProps.Templates.archiveError) {
829
+ const { successMessage, description } = nextProps.Templates.archiveSuccessPayload || {};
830
+ if (successMessage) CapNotification.success({ message: successMessage, description });
831
+ this.getAllTemplates({ params, resetPage: true });
832
+ }
833
+
834
+ const wasUnarchiving = this.props.Templates.unarchiveInProgress;
835
+ const isUnarchiveDone = !nextProps.Templates.unarchiveInProgress && wasUnarchiving;
836
+ if (isUnarchiveDone && !nextProps.Templates.unarchiveError) {
837
+ const { successMessage, description } = nextProps.Templates.unarchiveSuccessPayload || {};
838
+ if (successMessage) CapNotification.success({ message: successMessage, description });
839
+ this.getAllTemplates({ params, resetPage: true });
840
+ }
841
+
842
+ const wasBulkArchiving = this.props.Templates.bulkArchiveInProgress;
843
+ const isBulkArchiveDone = !nextProps.Templates.bulkArchiveInProgress && wasBulkArchiving;
844
+ if (isBulkArchiveDone && !nextProps.Templates.bulkArchiveError) {
845
+ const { count } = nextProps.Templates.bulkArchiveSuccessPayload || {};
846
+ CapNotification.success({ message: this.props.intl.formatMessage(messages.bulkArchiveSuccess, { count }) });
847
+ this.getAllTemplates({ params, resetPage: true });
848
+ }
849
+
850
+ const wasBulkUnarchiving = this.props.Templates.bulkUnarchiveInProgress;
851
+ const isBulkUnarchiveDone = !nextProps.Templates.bulkUnarchiveInProgress && wasBulkUnarchiving;
852
+ if (isBulkUnarchiveDone && !nextProps.Templates.bulkUnarchiveError) {
853
+ const { count } = nextProps.Templates.bulkUnarchiveSuccessPayload || {};
854
+ CapNotification.success({ message: this.props.intl.formatMessage(messages.bulkUnarchiveSuccess, { count }) });
855
+ this.getAllTemplates({ params, resetPage: true });
856
+ }
857
+
830
858
  if (!nextProps.Templates.sendingFile && !isEqual(this.props.Templates.sendingFile, nextProps.Templates.sendingFile) && !nextProps.Templates.errorSendingFile) {
831
859
  const module = this.props.location.query.module ? this.props.location.query.module : 'default';
832
860
  const isLanguageSupport = (this.props.location.query.isLanguageSupport) ? this.props.location.query.isLanguageSupport : true;
@@ -1595,6 +1623,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1595
1623
  let queryParams = params || {};
1596
1624
  let page = this.state.page;
1597
1625
  const { activeMode } = this.state;
1626
+ // Archive filter — use explicit param if provided (props may not have updated yet due to async dispatch)
1627
+ if (!queryParams.archiveStatus) {
1628
+ const archiveFilter = get(this.props, 'Templates.archiveFilter', 'active');
1629
+ queryParams.archiveStatus = archiveFilter;
1630
+ }
1598
1631
  if (activeMode === ACCOUNT_SELECTION_MODE) {
1599
1632
  this.setTemplatesMode();
1600
1633
  }
@@ -1861,6 +1894,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1861
1894
  const currentChannel = channel.toUpperCase();
1862
1895
  const {channel: stateChannel} = this.state;
1863
1896
  const channelLowerCase = stateChannel.toLowerCase();
1897
+ const _selectedIds = get(this.props, 'Templates.selectedTemplateIds', []);
1898
+ const _selectedIdsArray = _selectedIds && typeof _selectedIds.toJS === 'function' ? _selectedIds.toJS() : (Array.isArray(_selectedIds) ? _selectedIds : []);
1899
+ const hasSelection = this.props.isFullMode && _selectedIdsArray.length > 0;
1864
1900
  let filteredTemplates = templates;
1865
1901
  let isTraiDltFeature = false;
1866
1902
  switch (currentChannel) {
@@ -1900,47 +1936,59 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1900
1936
  const iosBodyType = get(template, 'versions.base.content.IOS.bodyType');
1901
1937
  const inappBodyType = androidBodyType || iosBodyType;
1902
1938
  const isZaloPreviewLoading = previewTemplateId === template?._id;
1939
+ const selectedIdsForCard = get(this.props, 'Templates.selectedTemplateIds', []);
1940
+ const selectedIdsArrayForCard = selectedIdsForCard.toJS ? selectedIdsForCard.toJS() : selectedIdsForCard;
1941
+ // Archive eligibility per template: Zalo never; WhatsApp/RCS not when pending/awaiting
1942
+ const cardWhatsappStatus = get(template, `versions.base.content.${WHATSAPP_LOWERCASE}.status`, '');
1943
+ const cardRcsStatus = get(template, 'versions.base.content.RCS.rcsContent.cardContent[0].Status', '');
1944
+ const isArchivalEnabled = commonUtil.hasCreativesArchivalEnabled();
1945
+ const isCardArchiveEligible = isArchivalEnabled && this.isChannelArchiveEligible(currentChannel, cardWhatsappStatus, cardRcsStatus);
1946
+ const isArchivedMode = isArchivalEnabled && get(this.props, 'Templates.isArchivedMode', false);
1947
+ const isAnyArchiveInProgress = isArchivalEnabled && !!(get(this.props, 'Templates.archiveInProgress') || get(this.props, 'Templates.unarchiveInProgress') || get(this.props, 'Templates.bulkArchiveInProgress') || get(this.props, 'Templates.bulkUnarchiveInProgress'));
1903
1948
  const templateData = {
1904
1949
  key: `${currentChannel}-card-${template?.name}`,
1905
1950
  title: (
1906
- <span title={template?.name}>
1907
- {template?.name}
1908
- {currentChannel === INAPP && (
1909
- <CapRow useLegacy>
1910
- <CapColoredTag
1911
- tagColor={INAPP_LAYOUT_DETAILS[inappBodyType]?.tagColor}
1912
- tagTextColor={
1913
- INAPP_LAYOUT_DETAILS[inappBodyType]?.tagTextColor
1914
- }
1915
- tagHeight="1.25rem"
1916
- tagFontSize="12px"
1917
- >
1918
- {INAPP_LAYOUT_DETAILS[inappBodyType]?.text}
1919
- </CapColoredTag>
1920
- </CapRow>
1921
- )}
1951
+ <span className="template-card-title" title={template?.name}>
1952
+ {isCardArchiveEligible && this.renderCardSelectionCheckbox({ templateId: template._id, selectedIds: selectedIdsArrayForCard, isDisabled: isAnyArchiveInProgress })}
1953
+ <CapLabel.CapLabelInline type="label1" title={template?.name} className="template-card-name">
1954
+ {template?.name}
1955
+ {currentChannel === INAPP && (
1956
+ <CapRow useLegacy>
1957
+ <CapColoredTag
1958
+ tagColor={INAPP_LAYOUT_DETAILS[inappBodyType]?.tagColor}
1959
+ tagTextColor={
1960
+ INAPP_LAYOUT_DETAILS[inappBodyType]?.tagTextColor
1961
+ }
1962
+ tagHeight="1.25rem"
1963
+ tagFontSize="12px"
1964
+ >
1965
+ {INAPP_LAYOUT_DETAILS[inappBodyType]?.text}
1966
+ </CapColoredTag>
1967
+ </CapRow>
1968
+ )}
1969
+ </CapLabel.CapLabelInline>
1922
1970
  </span>
1923
1971
  ),
1924
- extra: [
1972
+ extra: isArchivedMode ? [] : [
1925
1973
  // Hide preview icon for channels that support Test and Preview
1926
1974
  // Show preview icon only for channels that don't support Test and Preview
1927
1975
  (() => {
1928
1976
  // Channels that have Test and Preview integrated
1929
1977
  const testAndPreviewChannels = [EMAIL, SMS, WHATSAPP, RCS, INAPP, MOBILE_PUSH, VIBER, ZALO];
1930
1978
  const isTestAndPreviewSupported = testAndPreviewChannels.includes(currentChannel.toUpperCase());
1931
-
1979
+
1932
1980
  // Don't show preview icon if channel supports Test and Preview
1933
1981
  if (isTestAndPreviewSupported) {
1934
1982
  return null;
1935
1983
  }
1936
-
1984
+
1937
1985
  // Show preview icon for other channels (e.g., WeChat, Line, Facebook, Ebill)
1938
1986
  if (currentChannel === ZALO && isZaloPreviewLoading) {
1939
1987
  return (
1940
1988
  <CapSpin style={{ position: "static", display: "inline" }} spinning />
1941
1989
  );
1942
1990
  }
1943
-
1991
+
1944
1992
  return this.getHoverComponent(
1945
1993
  <CapIcon
1946
1994
  className={`view-${channelLowerCase}`}
@@ -1960,7 +2008,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1960
2008
  );
1961
2009
  })()
1962
2010
  ],
1963
- hoverOption: (
2011
+ hoverOption: isArchivedMode ? null : (
1964
2012
  <CapButton
1965
2013
  className={
1966
2014
  this.props.isFullMode
@@ -2000,8 +2048,8 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2000
2048
  <CapDropdown
2001
2049
  overlay={
2002
2050
  <CapMenu>
2003
- {/* Phase 16: Test and Preview menu item - Show for supported channels */}
2004
- {(this.isTestAndPreviewSupported() ||
2051
+ {/* Phase 16: Test and Preview menu item - Show for supported channels, hide in archived mode */}
2052
+ {!isArchivedMode && (this.isTestAndPreviewSupported() ||
2005
2053
  (this.state.channel.toUpperCase() === WHATSAPP &&
2006
2054
  status === WHATSAPP_STATUSES.approved) || (this.state.channel.toUpperCase() === RCS &&
2007
2055
  rcsStatus === RCS_STATUSES.approved)) && (
@@ -2024,7 +2072,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2024
2072
  {![WECHAT, WHATSAPP, ZALO].includes(
2025
2073
  this.state.channel.toUpperCase()
2026
2074
  ) &&
2027
- !isTraiDltFeature && (
2075
+ !isTraiDltFeature && !template.isArchived && (
2028
2076
  <CapMenu.Item
2029
2077
  className={`duplicate-${channelLowerCase}`}
2030
2078
  onClick={() => this.duplicateTemplate(template)}
@@ -2032,6 +2080,26 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2032
2080
  <FormattedMessage {...messages.duplicateButton} />
2033
2081
  </CapMenu.Item>
2034
2082
  )}
2083
+ {/* Archive/Unarchive menu item (full mode only, not for Zalo, not for WhatsApp/RCS awaiting/pending) */}
2084
+ {commonUtil.hasCreativesArchivalEnabled() && (() => {
2085
+ const channelUp = this.state.channel.toUpperCase();
2086
+ if (!this.isChannelArchiveEligible(channelUp, status, rcsStatus)) return null;
2087
+ return !template?.isArchived ? (
2088
+ <CapMenu.Item
2089
+ className={`archive-${channelLowerCase}`}
2090
+ onClick={() => this.handleTemplateArchiveAction({ templateId: template._id, templateName: template.name })}
2091
+ >
2092
+ <FormattedMessage {...messages.archiveButton} />
2093
+ </CapMenu.Item>
2094
+ ) : (
2095
+ <CapMenu.Item
2096
+ className={`unarchive-${channelLowerCase}`}
2097
+ onClick={() => this.handleTemplateArchiveAction({ templateId: template._id, templateName: template.name, isUnarchive: true })}
2098
+ >
2099
+ <FormattedMessage {...messages.unarchiveButton} />
2100
+ </CapMenu.Item>
2101
+ );
2102
+ })()}
2035
2103
  {/* Delete/Unmap menu item */}
2036
2104
  {(!(
2037
2105
  this.state.channel.toUpperCase() === WHATSAPP &&
@@ -2311,8 +2379,10 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2311
2379
  }
2312
2380
  case WHATSAPP: {
2313
2381
  const { whatsappImageSrc = '', templateMsg, docPreview, whatsappVideoPreviewImg = '', templateHeaderPreview, templateFooterPreview, carouselData = [] } = getWhatsappContent(template);
2382
+ const checkBoxWrapperClassName = 'whatsapp-card-head-checkbox-wrapper'
2314
2383
  templateData.title = (
2315
- <CapRow useLegacy>
2384
+ <CapRow type='flex' align="middle" justify="start" gap={12}>
2385
+ {isCardArchiveEligible && this.renderCardSelectionCheckbox({ templateId: template._id, selectedIds: selectedIdsArrayForCard, isDisabled: isAnyArchiveInProgress, className: checkBoxWrapperClassName })}
2316
2386
  <CapLabel className="whatsapp-rcs-template-name">{template?.name}</CapLabel>
2317
2387
  <WhatsappStatusContainer template={template} />
2318
2388
  </CapRow>
@@ -2405,7 +2475,8 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2405
2475
  const name = get(template, "name", "");
2406
2476
  const statusDisplay=getRcsStatusType(status);
2407
2477
  templateData.title = (
2408
- <CapRow useLegacy>
2478
+ <CapRow type='flex' align="middle" justify="start" gap={12}>
2479
+ {isCardArchiveEligible && this.renderCardSelectionCheckbox({ templateId: template._id, selectedIds: selectedIdsArrayForCard, isDisabled: isAnyArchiveInProgress })}
2409
2480
  <CapLabel className="whatsapp-rcs-template-name">{name}</CapLabel>
2410
2481
  <CapColumn type="flex" align="middle" className="rcs-status-container zalo-status-color">
2411
2482
  <CapStatus
@@ -2529,7 +2600,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2529
2600
 
2530
2601
  //no templates available
2531
2602
  const showIllustrationForChannel = (forChannel) => {
2532
- return forChannel === channelLowerCase && isEmpty(templates) && isEmpty(this.state.searchText) && !isLoading;
2603
+ return forChannel === channelLowerCase && isEmpty(templates) && isEmpty(this.state.searchText) && !isLoading && !get(this.props, 'Templates.isArchivedMode', false);
2533
2604
  }
2534
2605
  //when filters applied not matching templates
2535
2606
  const filteredEmptyAndFullModeAs = (fullModeValue) => {
@@ -2547,6 +2618,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2547
2618
  const accountId = get(this.props, 'Templates.selectedWeChatAccount.uuid');
2548
2619
  const accounts = get(this.props, 'Templates.weCrmAccounts');
2549
2620
  const getAllTemplatesInProgress = get(this.props, 'Templates.getAllTemplatesInProgress');
2621
+ const archiveListingRefreshType = get(this.props, 'Templates.archiveListingRefreshType', null);
2622
+ const isArchiveOperationInProgress = get(this.props, 'Templates.archiveInProgress', false)
2623
+ || get(this.props, 'Templates.unarchiveInProgress', false)
2624
+ || get(this.props, 'Templates.bulkArchiveInProgress', false)
2625
+ || get(this.props, 'Templates.bulkUnarchiveInProgress', false);
2550
2626
 
2551
2627
  const noWhatsappZaloTemplates = this.isFullMode() && isEmpty(templates) || !this.state.hostName;
2552
2628
  const noApprovedWhatsappZaloTemplates = filteredEmptyAndFullModeAs(false) && !isEmpty(this.state?.hostName);
@@ -2555,9 +2631,37 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2555
2631
 
2556
2632
  const noLoaderAndSearchText = isEmpty(this.state.searchText) && !isLoading;
2557
2633
 
2558
- return (<div>
2634
+ const isArchivalEnabledLocal = commonUtil.hasCreativesArchivalEnabled();
2635
+ const isArchivedModeLocal = isArchivalEnabledLocal && get(this.props, 'Templates.isArchivedMode', false);
2636
+ const selectedIdsLocal = get(this.props, 'Templates.selectedTemplateIds', []);
2637
+ const selectedIdsArrayLocal = selectedIdsLocal && typeof selectedIdsLocal.toJS === 'function' ? selectedIdsLocal.toJS() : (Array.isArray(selectedIdsLocal) ? selectedIdsLocal : []);
2638
+ const selectedCountLocal = selectedIdsArrayLocal.length;
2639
+ const hasSelectionLocal = isArchivalEnabledLocal && this.props.isFullMode && selectedCountLocal > 0;
2640
+
2641
+ return (<div>
2559
2642
  {[WECHAT, MOBILE_PUSH, WEBPUSH, INAPP, WHATSAPP, ZALO, RCS].includes(currentChannel) && this.showAccountName()}
2560
- {filterContent}
2643
+ <CapRow type="flex" align="middle" justify="space-between" className="filter-row">
2644
+ <div className="filter-row-content">{filterContent}</div>
2645
+ {hasSelectionLocal && (
2646
+ <CapRow type="flex" align="middle" className="bulk-selection-bar">
2647
+ <CapLabel type="label2">
2648
+ {this.props.intl.formatMessage(messages.templatesSelected, { count: selectedCountLocal })}
2649
+ </CapLabel>
2650
+ <CapButton
2651
+ type="primary"
2652
+ prefix={<CapIcon type="archive" size="m" />}
2653
+ onClick={() => this.handleBulkArchiveAction({ ids: selectedIdsArrayLocal, count: selectedCountLocal, isUnarchive: isArchivedModeLocal })}
2654
+ >
2655
+ <span className="archive-btn-label">
2656
+ <FormattedMessage {...(isArchivedModeLocal ? messages.unarchiveButton : messages.archiveButton)} />
2657
+ </span>
2658
+ </CapButton>
2659
+ <CapButton type="secondary" onClick={() => this.props.actions.clearTemplateSelection()}>
2660
+ <FormattedMessage {...messages.archiveConfirmCancel} />
2661
+ </CapButton>
2662
+ </CapRow>
2663
+ )}
2664
+ </CapRow>
2561
2665
  {[WHATSAPP, ZALO, INAPP,RCS].includes(currentChannel) && this.selectedFilters()}
2562
2666
  {<div>
2563
2667
  {!isEmpty(filteredTemplates) || !isEmpty(this.state.searchText) || !isEmpty(this.props.Templates.templateError) ? (
@@ -2570,7 +2674,7 @@ return (<div>
2570
2674
  fbType={"list"}
2571
2675
  />
2572
2676
  </div>)
2573
- : [SMS_LOWERCASE, EMAIL_LOWERCASE].includes(this.state.channel.toLowerCase()) && !isLoading && this.getSmsEmailIllustration()
2677
+ : [SMS_LOWERCASE, EMAIL_LOWERCASE].includes(this.state.channel.toLowerCase()) && !isLoading && !get(this.props, 'Templates.isArchivedMode', false) && this.getSmsEmailIllustration()
2574
2678
  }
2575
2679
 
2576
2680
  {(this.state.selectedAccount === '' && isEmpty(this.props.Templates.selectedWeChatAccount)) && ([WECHAT_LOWERCASE, MOBILE_PUSH_LOWERCASE, INAPP_LOWERCASE].includes(this.state.channel.toLowerCase())) &&
@@ -2594,7 +2698,7 @@ return (<div>
2594
2698
  </div>
2595
2699
  )
2596
2700
  }
2597
- {(showWhatsappIllustration || showZaloIllustration) && (
2701
+ {(showWhatsappIllustration || showZaloIllustration) && !get(this.props, 'Templates.isArchivedMode', false) && (
2598
2702
  noLoaderAndSearchText &&
2599
2703
  <div style={this.isFullMode() ? { height: "calc(100vh - 325px)", overflow: 'auto' } : {}}>
2600
2704
  {noWhatsappZaloTemplates && <ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={currentChannel} hostName={this.state?.hostName}/>}
@@ -2635,8 +2739,27 @@ return (<div>
2635
2739
  <ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={currentChannel} hostName={this.state?.hostName}/>
2636
2740
  </div>
2637
2741
  }
2742
+ {get(this.props, 'Templates.isArchivedMode', false) && isEmpty(templates) && !isLoading && !getAllTemplatesInProgress && isEmpty(this.state.searchText) && (
2743
+ <CapRow className={this.isFullMode() ? 'illustration-scroll-wrapper' : ''}>
2744
+ <ChannelTypeIllustration
2745
+ isFullMode={this.props.isFullMode}
2746
+ createTemplate={this.createTemplate}
2747
+ currentChannel={currentChannel}
2748
+ isArchivedMode
2749
+ />
2750
+ </CapRow>
2751
+ )}
2638
2752
  {<CapCustomSkeleton loader={isInitialLoading && (isLoading || getAllTemplatesInProgress)} />}
2639
- {<CapPageSpinner spinning={!isInitialLoading && (isLoading || getAllTemplatesInProgress)} />}
2753
+ {!isInitialLoading && getAllTemplatesInProgress && archiveListingRefreshType ? (
2754
+ <div className="archive-listing-spinner">
2755
+ <CapSpin spinning />
2756
+ <CapLabel.CapLabelInline type="label1">
2757
+ {archiveListingRefreshType === ARCHIVE_REFRESH_TYPE_ARCHIVE ? this.props.intl.formatMessage(messages.archivalInProgress) : this.props.intl.formatMessage(messages.unarchivalInProgress)}
2758
+ </CapLabel.CapLabelInline>
2759
+ </div>
2760
+ ) : (
2761
+ <CapPageSpinner spinning={!isInitialLoading && (isLoading || getAllTemplatesInProgress)} />
2762
+ )}
2640
2763
  </div>
2641
2764
  }
2642
2765
  </div>);
@@ -3278,6 +3401,10 @@ return (<div>
3278
3401
  };
3279
3402
 
3280
3403
  handleEditClick(e, template, modeType, path, options) {
3404
+ if (template && template.isArchived) {
3405
+ CapNotification.error({ message: this.props.intl.formatMessage(messages.cannotEditArchivedTemplate) });
3406
+ return;
3407
+ }
3281
3408
  if (modeType && modeType !== undefined) {
3282
3409
  this.setState({modeType});
3283
3410
  }
@@ -3459,6 +3586,86 @@ return (<div>
3459
3586
  this.setState({showModal: false});
3460
3587
  }
3461
3588
 
3589
+ renderCardSelectionCheckbox = ({ templateId, selectedIds, isDisabled, className }) => {
3590
+ if (!this.props.isFullMode || this.props.location.query.type === EMBEDDED) return null;
3591
+ return (
3592
+ <CapCheckbox
3593
+ className={className}
3594
+ checked={selectedIds.includes(templateId)}
3595
+ onChange={() => !isDisabled && this.props.actions.toggleTemplateSelection(templateId)}
3596
+ onClick={(e) => e.stopPropagation()}
3597
+ disabled={isDisabled}
3598
+ />
3599
+ );
3600
+ }
3601
+
3602
+ handleBulkArchiveAction = ({ ids, count, isUnarchive = false }) => {
3603
+ const { intl, actions } = this.props;
3604
+ const { channel } = this.state;
3605
+ const title = isUnarchive
3606
+ ? intl.formatMessage(messages.unarchiveTemplates)
3607
+ : intl.formatMessage(messages.archiveTemplates);
3608
+ const action = isUnarchive ? actions.bulkUnarchiveTemplates : actions.bulkArchiveTemplates;
3609
+ const successMessage = (c) => intl.formatMessage(
3610
+ isUnarchive ? messages.bulkUnarchiveSuccess : messages.bulkArchiveSuccess,
3611
+ { count: c }
3612
+ );
3613
+ this.showArchiveConfirm({
3614
+ title,
3615
+ count,
3616
+ isUnarchive,
3617
+ onConfirm: () => action(channel, ids, successMessage),
3618
+ });
3619
+ };
3620
+
3621
+ handleTemplateArchiveAction = ({ templateId, templateName, isUnarchive = false }) => {
3622
+ const { intl, actions } = this.props;
3623
+ const { channel } = this.state;
3624
+ const title = isUnarchive
3625
+ ? intl.formatMessage(messages.unarchiveTemplates)
3626
+ : intl.formatMessage(messages.archiveTemplates);
3627
+ const successMessage = isUnarchive
3628
+ ? intl.formatMessage(messages.unarchiveTemplateSuccess)
3629
+ : intl.formatMessage(messages.archiveTemplateSuccess);
3630
+ const action = isUnarchive ? actions.unarchiveTemplate : actions.archiveTemplate;
3631
+ this.showArchiveConfirm({
3632
+ title,
3633
+ count: 1,
3634
+ isUnarchive,
3635
+ onConfirm: () => action(
3636
+ channel,
3637
+ templateId,
3638
+ successMessage,
3639
+ buildTemplateNameDescription(intl.formatMessage(messages.templateNameLabel), templateName)
3640
+ ),
3641
+ });
3642
+ };
3643
+
3644
+ // Shared helper for archive/unarchive confirm modals — opens the themed
3645
+ // <CapModal> via state so the footer uses CapButton (green primary).
3646
+ showArchiveConfirm = ({ title, content, onConfirm, count = 1, isUnarchive = false }) => {
3647
+ const { intl } = this.props;
3648
+ const resolvedContent = content || (isUnarchive
3649
+ ? intl.formatMessage(count > 1 ? messages.unarchiveTemplateContent : messages.unarchiveTemplateSingleContent)
3650
+ : intl.formatMessage(count > 1 ? messages.archiveTemplateContent : messages.archiveTemplateSingleContent));
3651
+ this.setState({
3652
+ showArchiveModal: true,
3653
+ archiveModalContext: { title, content: resolvedContent, onConfirm },
3654
+ });
3655
+ }
3656
+
3657
+ handleArchiveModalOk = () => {
3658
+ const { archiveModalContext } = this.state;
3659
+ this.setState({ showArchiveModal: false, archiveModalContext: null });
3660
+ if (archiveModalContext && archiveModalContext.onConfirm) {
3661
+ archiveModalContext.onConfirm();
3662
+ }
3663
+ }
3664
+
3665
+ handleArchiveModalCancel = () => {
3666
+ this.setState({ showArchiveModal: false, archiveModalContext: null });
3667
+ }
3668
+
3462
3669
  populateTemplatesList = (data, blankTemplateRequired, layoutSelection) => {
3463
3670
  if (!data) {
3464
3671
  return [];
@@ -3608,22 +3815,71 @@ return (<div>
3608
3815
  deleteOption = this.props.intl.formatMessage(messages.unMapButton);
3609
3816
  }
3610
3817
  if (!layoutSelection) {
3818
+ // Determine archive eligibility for this template:
3819
+ // - Zalo: never eligible
3820
+ // - WhatsApp: not eligible when status is awaitingApproval / pending / unsubmitted
3821
+ // - RCS: not eligible when status is awaitingApproval / pending
3822
+ const templateWhatsappStatus = get(template, `versions.base.content.${WHATSAPP_LOWERCASE}.status`, '');
3823
+ const templateRcsStatus = get(template, 'versions.base.content.RCS.rcsContent.cardContent[0].Status', '');
3824
+ const channelUpCase = this.state.channel.toUpperCase();
3825
+ const isTemplateArchiveEligible = this.isChannelArchiveEligible(channelUpCase, templateWhatsappStatus, templateRcsStatus);
3826
+
3827
+ // Checkbox on card header (full mode only, only for archive-eligible templates)
3828
+ if (this.props.isFullMode && this.props.location.query.type !== EMBEDDED && isTemplateArchiveEligible) {
3829
+ const selectedIds = get(this.props, 'Templates.selectedTemplateIds', []);
3830
+ const selectedIdsArray = selectedIds.toJS ? selectedIds.toJS() : selectedIds;
3831
+ temp.cardTop = (
3832
+ <CapRow type="flex" align="middle" justify="space-between" className="template-card-top-bar">
3833
+ <CapCheckbox
3834
+ checked={selectedIdsArray.includes(template._id)}
3835
+ onChange={() => this.props.actions.toggleTemplateSelection(template._id)}
3836
+ onClick={(e) => e.stopPropagation()}
3837
+ />
3838
+ </CapRow>
3839
+ );
3840
+ }
3841
+
3611
3842
  temp.footer = (
3612
3843
  <div className="footer-container">
3613
3844
  <div className="card-title">
3614
3845
  <span className="template-name" style={{ fontWeight: `${this.state.channel.toLowerCase() === 'wechat' ? '400' : '600'}` }}>
3615
3846
  { 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>}
3616
3847
  {template?.name}
3848
+ {commonUtil.hasCreativesArchivalEnabled() && template.isArchived && <CapColoredTag tagColor={CAP_G08} tagTextColor={CAP_G05} className="archived-tag">{this.props.intl.formatMessage(messages.archivedTag)}</CapColoredTag>}
3617
3849
  </span>
3618
- {this.props.location.query.type !== 'embedded' && <CapPopover
3850
+ {this.props.location.query.type !== EMBEDDED && <CapPopover
3619
3851
  trigger="click"
3620
3852
  content={
3621
3853
  <div className="popover-content">
3622
- {this.state.channel !== 'wechat' && <div className="popover-action-container">
3623
- <span onClick={() => this.duplicateTemplate(template)} className="popover-action" style={{cursor: 'pointer', padding: '8px 0px'}}>{this.props.intl.formatMessage(messages.duplicateButton)}</span>
3854
+ {this.state.channel !== 'wechat' && !template.isArchived && <div className="popover-action-container">
3855
+ <CapButton type="link" onClick={() => this.duplicateTemplate(template)} className="popover-action">
3856
+ {this.props.intl.formatMessage(messages.duplicateButton)}
3857
+ </CapButton>
3858
+ </div>}
3859
+ {commonUtil.hasCreativesArchivalEnabled() && this.props.isFullMode && isTemplateArchiveEligible && !template.isArchived && <div className="popover-action-container">
3860
+ <CapButton
3861
+ type="link"
3862
+ onClick={() => this.handleTemplateArchiveAction({ templateId: template._id, templateName: template.name })}
3863
+ className="popover-action popover-archive-action"
3864
+ >
3865
+ <CapIcon type="archive" size="s" />
3866
+ <CapLabel.CapLabelInline type="label1">{this.props.intl.formatMessage(messages.archiveButton)}</CapLabel.CapLabelInline>
3867
+ </CapButton>
3868
+ </div>}
3869
+ {commonUtil.hasCreativesArchivalEnabled() && this.props.isFullMode && isTemplateArchiveEligible && template.isArchived && <div className="popover-action-container">
3870
+ <CapButton
3871
+ type="link"
3872
+ onClick={() => this.handleTemplateArchiveAction({ templateId: template._id, templateName: template.name, isUnarchive: true })}
3873
+ className="popover-action popover-archive-action"
3874
+ >
3875
+ <CapIcon type="archive" size="s" />
3876
+ <CapLabel.CapLabelInline type="label1">{this.props.intl.formatMessage(messages.unarchiveButton)}</CapLabel.CapLabelInline>
3877
+ </CapButton>
3624
3878
  </div>}
3625
3879
  <div className="popover-action-container">
3626
- <span onClick={() => this.toggleDeleteTemplateModal(template)} className="popover-action" style={{cursor: 'pointer', padding: '8px 0px'}}>{deleteOption}</span>
3880
+ <CapButton type="link" onClick={() => this.toggleDeleteTemplateModal(template)} className="popover-action">
3881
+ {deleteOption}
3882
+ </CapButton>
3627
3883
  </div>
3628
3884
  </div>
3629
3885
  }
@@ -3676,7 +3932,20 @@ return (<div>
3676
3932
  return false;
3677
3933
  }
3678
3934
  }
3679
- isFullMode = () => this.props.location.query.type !== "embedded" || this.props.isFullMode
3935
+ isFullMode = () => this.props.location.query.type !== EMBEDDED || this.props.isFullMode
3936
+
3937
+ isChannelArchiveEligible = (channel, whatsappStatus, rcsStatus) => (
3938
+ channel !== ZALO &&
3939
+ !(channel === WHATSAPP && [WHATSAPP_STATUSES.awaitingApproval, WHATSAPP_STATUSES.pending, WHATSAPP_STATUSES.unsubmitted].includes(whatsappStatus)) &&
3940
+ !(channel === RCS && [RCS_STATUSES.awaitingApproval, RCS_STATUSES.pending].includes(rcsStatus))
3941
+ )
3942
+
3943
+ setArchivedMode = (isArchived) => {
3944
+ this.props.actions.setArchivedMode(isArchived);
3945
+ this.setState({ searchText: '', page: 1 }, () => {
3946
+ this.getAllTemplates({ params: { name: '', sortBy: this.state.sortBy, archiveStatus: isArchived ? ARCHIVE_STATUS_ARCHIVED : ARCHIVE_STATUS_ACTIVE }, resetPage: true }, true);
3947
+ });
3948
+ }
3680
3949
  isCreateDisabled = () => {
3681
3950
  let isDisabled = this.isLoading();
3682
3951
  const channel = this.state.channel.toUpperCase();
@@ -3816,6 +4085,26 @@ return (<div>
3816
4085
  return modal;
3817
4086
  };
3818
4087
 
4088
+ renderArchiveConfirmationModal = () => {
4089
+ const { intl: { formatMessage } = {} } = this.props;
4090
+ const { showArchiveModal, archiveModalContext } = this.state;
4091
+ if (!archiveModalContext) return null;
4092
+ return (
4093
+ <CapModal
4094
+ className="archive-confirm-modal"
4095
+ title={archiveModalContext.title}
4096
+ visible={showArchiveModal}
4097
+ onOk={this.handleArchiveModalOk}
4098
+ onCancel={this.handleArchiveModalCancel}
4099
+ centered
4100
+ okText={formatMessage(messages.archiveConfirmOk)}
4101
+ closeText={formatMessage(messages.archiveConfirmCancel)}
4102
+ >
4103
+ {archiveModalContext.content}
4104
+ </CapModal>
4105
+ );
4106
+ };
4107
+
3819
4108
  renderAccountSelection = () => {
3820
4109
  const accountOptions = [];
3821
4110
  const { selectedAccount } = this.state;
@@ -3922,7 +4211,20 @@ return (<div>
3922
4211
  default:
3923
4212
  break;
3924
4213
  }
4214
+ // LOCAL BYPASS — revert before push: fake a WeChat account so empty-state is skipped
4215
+ if (channel === WECHAT && isEmpty(accountOptions) && !fetchingWeCrmAccounts) {
4216
+ accountOptions.push({
4217
+ key: 'local-bypass-wechat',
4218
+ value: 'Local WeChat (bypass)',
4219
+ title: 'Local WeChat (bypass)',
4220
+ icon: <CapIcon.CapIconAvatar text="L" width="auto" height="2rem"/>,
4221
+ });
4222
+ }
3925
4223
  let showNoAccountHeader = isEmpty(weCrmAccounts) && !fetchingWeCrmAccounts;
4224
+ // LOCAL BYPASS — revert before push: force account-selection UI for WeChat
4225
+ if (channel === WECHAT) {
4226
+ showNoAccountHeader = false;
4227
+ }
3926
4228
  // Zalo, Whatsapp and RCS have dependencies on domainProperties to get the hostName. Show loader until the domainProperties are fetched.
3927
4229
  const isDomainPropertiesLoading = [WHATSAPP, ZALO, RCS].includes(channel) && senderDetails?.status === "REQUEST";
3928
4230
  if (channel === FACEBOOK && !isEmpty(campaignSettings) ) {
@@ -4214,11 +4516,17 @@ return (<div>
4214
4516
  if (([WHATSAPP_LOWERCASE, ZALO_LOWERCASE, RCS_LOWERCASE].includes(this.state?.channel?.toLocaleLowerCase()) && isEmpty(this.state?.hostName))) {
4215
4517
  isfilterContentVisisble = false;
4216
4518
  }
4519
+ const _isArchivalEnabled = commonUtil.hasCreativesArchivalEnabled();
4520
+ const _isArchivedMode = _isArchivalEnabled && get(this.props, 'Templates.isArchivedMode', false);
4521
+ const _renderSelectedIds = get(this.props, 'Templates.selectedTemplateIds', []);
4522
+ const _renderSelectedIdsArray = _renderSelectedIds && typeof _renderSelectedIds.toJS === 'function' ? _renderSelectedIds.toJS() : (Array.isArray(_renderSelectedIds) ? _renderSelectedIds : []);
4523
+ const _renderHasSelection = _isArchivalEnabled && this.props.isFullMode && _renderSelectedIdsArray.length > 0;
4524
+
4217
4525
  const filterContent = (( isfilterContentVisisble || [WECHAT, MOBILE_PUSH, INAPP].includes(this.state.channel.toUpperCase())) && <div className="action-container">
4218
4526
  {isfilterContentVisisble && <CapInput.Search
4219
4527
  className="search-text"
4220
4528
  style={{width: '210px'}}
4221
- placeholder={this.props.intl.formatMessage(messages.searchText)}
4529
+ placeholder={_isArchivedMode ? this.props.intl.formatMessage(messages.searchArchivedTemplates) : this.props.intl.formatMessage(messages.searchText)}
4222
4530
  value={this.state.searchText}
4223
4531
  onChange={(e) => this.searchTemplate(e.target.value, this.state.channel)}
4224
4532
  disabled={this.checkSearchDisabled()}
@@ -4370,9 +4678,9 @@ return (<div>
4370
4678
  </div>
4371
4679
  )
4372
4680
  }
4373
- <div style={{display: "flex", justifyContent: "space-between", alignItems: 'center'}}>
4374
- {
4375
- this.state?.channel?.toLowerCase() === WHATSAPP_LOWERCASE && (isWhatsappCountExeeded)? (
4681
+ <div className="template-listing-header-actions">
4682
+ {!_isArchivedMode && !_renderHasSelection && (
4683
+ this.state?.channel?.toLowerCase() === WHATSAPP_LOWERCASE && (isWhatsappCountExeeded) ? (
4376
4684
  <CapTooltip title={whatsappCountExceedText}>
4377
4685
  <div className="button-disabled-tooltip-wrapper">
4378
4686
  {createButton}
@@ -4380,9 +4688,29 @@ return (<div>
4380
4688
  </CapTooltip>
4381
4689
  )
4382
4690
  : isfilterContentVisisble && !isWechatEmbedded && !this.props.isDltFromRcs && createButton
4383
- }
4691
+ )}
4692
+ {/* More (⋯) menu: full mode only, not archived mode, not Zalo (no archive support), not when selection active, archive flag enabled */}
4693
+ {commonUtil.hasCreativesArchivalEnabled() && !_isArchivedMode && !_renderHasSelection && this.props.isFullMode && this.props.location.query.type !== EMBEDDED && channelLowerCase !== ZALO_LOWERCASE && (
4694
+ <CapDropdown
4695
+ trigger={['click']}
4696
+ overlay={
4697
+ <CapMenu>
4698
+ <CapMenu.Item
4699
+ key="archived"
4700
+ onClick={() => this.setArchivedMode(true)}
4701
+ >
4702
+ <FormattedMessage {...messages.archivedTemplates} />
4703
+ </CapMenu.Item>
4704
+ </CapMenu>
4705
+ }
4706
+ placement="bottomRight"
4707
+ >
4708
+ <CapButton type="flat" className="template-listing-more-btn">
4709
+ <CapIcon type="more" />
4710
+ </CapButton>
4711
+ </CapDropdown>
4712
+ )}
4384
4713
  </div>
4385
-
4386
4714
  </div>);
4387
4715
  let htmlPreviewContent = "";
4388
4716
  if (this.state.channel.toLowerCase() === 'ebill') {
@@ -4431,6 +4759,18 @@ return (<div>
4431
4759
  }
4432
4760
  />
4433
4761
 
4762
+ {/* Archived mode header with back arrow (full mode only, archive flag enabled) */}
4763
+ {commonUtil.hasCreativesArchivalEnabled() && this.props.isFullMode && get(this.props, 'Templates.isArchivedMode', false) && (
4764
+ <CapRow type="flex" align="middle" className="archived-mode-header">
4765
+ <CapIcon
4766
+ type="back"
4767
+ className="archived-mode-back-icon"
4768
+ onClick={() => this.setArchivedMode(false)}
4769
+ />
4770
+ <CapHeading type="h3"><FormattedMessage {...messages.archivedTemplates} /></CapHeading>
4771
+ </CapRow>
4772
+ )}
4773
+
4434
4774
  {channel.toLowerCase() === WHATSAPP_LOWERCASE &&
4435
4775
  showWhatsappCountWarning ? (
4436
4776
  <CapAlert message={whatsappCountExceedText} type="info" />
@@ -4554,6 +4894,7 @@ return (<div>
4554
4894
  isLoading={this.props.Templates.getCmsTemplatesInProgress}
4555
4895
  />
4556
4896
  {this.renderDeleteConfirmationModal()}
4897
+ {this.renderArchiveConfirmationModal()}
4557
4898
  </CapRow>
4558
4899
  {/* Phase 17: Test and Preview Slidebox from Listing - Complete Integration */}
4559
4900
  {this.state.showTestAndPreviewSlidebox && (