@capillarytech/creatives-library 8.0.347 → 8.0.348

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 (38) hide show
  1. package/package.json +1 -1
  2. package/services/api.js +0 -20
  3. package/services/tests/api.test.js +0 -59
  4. package/utils/tests/v2Common.test.js +1 -46
  5. package/utils/v2common.js +0 -18
  6. package/v2Components/CapCustomSkeleton/index.js +1 -1
  7. package/v2Components/CapCustomSkeleton/tests/__snapshots__/index.test.js.snap +12 -12
  8. package/v2Components/CommonTestAndPreview/UnifiedPreview/WhatsAppPreviewContent.js +18 -6
  9. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +27 -0
  10. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/WhatsAppPreviewContent.test.js +48 -0
  11. package/v2Components/TemplatePreview/_templatePreview.scss +21 -0
  12. package/v2Components/TemplatePreview/index.js +18 -6
  13. package/v2Components/TemplatePreview/tests/__snapshots__/index.test.js.snap +1 -0
  14. package/v2Containers/CreativesContainer/SlideBoxFooter.js +1 -3
  15. package/v2Containers/CreativesContainer/index.js +9 -6
  16. package/v2Containers/CreativesContainer/messages.js +0 -4
  17. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +0 -3
  18. package/v2Containers/Templates/ChannelTypeIllustration.js +6 -23
  19. package/v2Containers/Templates/_templates.scss +24 -179
  20. package/v2Containers/Templates/actions.js +0 -44
  21. package/v2Containers/Templates/constants.js +0 -23
  22. package/v2Containers/Templates/index.js +58 -378
  23. package/v2Containers/Templates/messages.js +0 -88
  24. package/v2Containers/Templates/reducer.js +1 -84
  25. package/v2Containers/Templates/sagas.js +0 -64
  26. package/v2Containers/Templates/selectors.js +0 -12
  27. package/v2Containers/Templates/tests/ChannelTypeIllustration.test.js +0 -12
  28. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1122 -1345
  29. package/v2Containers/Templates/tests/index.test.js +0 -6
  30. package/v2Containers/Templates/tests/reducer.test.js +0 -178
  31. package/v2Containers/Templates/tests/sagas.test.js +8 -390
  32. package/v2Containers/Templates/tests/selector.test.js +0 -32
  33. package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -1
  34. package/v2Containers/Whatsapp/constants.js +8 -0
  35. package/v2Containers/Whatsapp/index.js +142 -5
  36. package/v2Containers/Whatsapp/index.scss +8 -0
  37. package/v2Containers/Whatsapp/messages.js +16 -0
  38. package/v2Containers/Assets/images/archive_Empty_Illustration.svg +0 -9
@@ -50,7 +50,9 @@ import {
50
50
  CapStatus,
51
51
  CapColoredTag,
52
52
  CapSpin,
53
- CapCheckbox,
53
+ CapCard,
54
+ CapColumn,
55
+ CapCarousel
54
56
  } from "@capillarytech/cap-ui-library";
55
57
  import { makeSelectTemplates, makeSelectTemplatesResponse } from './selectors';
56
58
  import { makeSelectCreate as makeSelectCreateSms } from '../Sms/Create/selectors';
@@ -81,7 +83,7 @@ import * as webpushActions from '../WebPush/actions';
81
83
  import * as globalActions from '../Cap/actions';
82
84
  import { makeSelectAuthenticated } from '../Cap/selectors';
83
85
  import { UserIsAuthenticated } from '../../utils/authWrapper';
84
- import { getObjFromQueryParams, buildTemplateNameDescription } from '../../utils/v2common';
86
+ import { getObjFromQueryParams } from '../../utils/v2common';
85
87
  import messages from './messages';
86
88
  import {checkUnicode} from '../../utils/smsCharCountV2';
87
89
  import { containsBase64Images } from '../../utils/content';
@@ -106,7 +108,7 @@ import {
106
108
  CREATE,
107
109
  } from '../App/constants';
108
110
  import {MAX_WHATSAPP_TEMPLATES, WARNING_WHATSAPP_TEMPLATES , ACCOUNT_MAPPING_ON_CHANNEL, noFilteredWhatsappZaloTemplatesTitle, noFilteredWhatsappZaloTemplatesDesc, noApprovedWhatsappZaloTemplatesTitle, noApprovedWhatsappTemplatesDesc, zaloDescIllustration, noApprovedRcsTemplatesTitle, noApprovedRcsTemplatesDesc} from './constants';
109
- import { COPY_OF, EMBEDDED } from '../../constants/unified';
111
+ import { COPY_OF } from '../../constants/unified';
110
112
  import {
111
113
  STATUS_OPTIONS,
112
114
  CATEGORY,
@@ -114,11 +116,19 @@ import {
114
116
  STATUS as WHATSAPP_STATUS,
115
117
  WHATSAPP_STATUSES,
116
118
  HOST_GUPSHUP,
119
+ HOST_HAPTIC,
117
120
  CATEGORY_OPTIONS_MAP,
121
+ HOST_TWILIO,
122
+ TWILIO_CATEGORY_OPTIONS,
123
+ KARIX_GUPSHUP_CATEGORY_OPTIONS,
124
+ ICS_CATEGORY_OPTIONS,
125
+ HAPTIC_CATEGORY_OPTIONS,
126
+ HOST_ICS,
118
127
  IMAGE,
119
128
  VIDEO,
129
+ GIF,
120
130
  } from '../Whatsapp/constants';
121
- import { INAPP_LAYOUT_DETAILS, INAPP_MESSAGE_LAYOUT_TYPES } from '../InApp/constants';
131
+ import { INAPP_LAYOUT_DETAILS, INAPP_MESSAGE_LAYOUT_TYPES, INAPP_MEDIA_TYPES, BIG_HTML, ANDROID, IOS } from '../InApp/constants';
122
132
  import { ZALO_STATUS_OPTIONS, ZALO_STATUSES } from '../Zalo/constants';
123
133
  import { getWhatsappContent, getWhatsappStatus, getWhatsappCategory, getWhatsappCta, getWhatsappQuickReply, getWhatsappAutoFill, getWhatsappCarouselButtonView } from '../Whatsapp/utils';
124
134
  import { getRCSContent } from '../Rcs/utils';
@@ -135,7 +145,7 @@ import {CREATIVE} from '../Facebook/constants';
135
145
  import videoPlay from '../../assets/videoPlay.svg';
136
146
  import whatsappImageEmptyPreview from '../../v2Components/TemplatePreview/assets/images/empty_image_preview.svg';
137
147
  import whatsappVideoEmptyPreview from '../../v2Components/TemplatePreview/assets/images/empty_video_preview.svg';
138
- import { CAP_SPACE_16, CAP_G08, CAP_G05, CAP_SPACE_08, CAP_SPACE_12 } from '@capillarytech/cap-ui-library/styled/variables';
148
+ import { CAP_SPACE_16 } from '@capillarytech/cap-ui-library/styled/variables';
139
149
  import { GA } from '@capillarytech/cap-ui-utils';
140
150
  import { MAPP_SDK } from '../InApp/constants';
141
151
  import injectReducer from '../../utils/injectReducer';
@@ -144,6 +154,7 @@ import { compose } from 'redux';
144
154
  import { v2TemplateSaga } from './sagas';
145
155
  import injectSaga from '../../utils/injectSaga';
146
156
  import { DAEMON } from '@capillarytech/vulcan-react-sdk/utils/sagaInjectorTypes';
157
+ import { Rcs } from '../Rcs';
147
158
  import { makeSelectRcs } from '../Rcs/selectors';
148
159
  import { getRcsStatusType } from '../Rcs/utils';
149
160
  import { makeSelectWebPush } from '../WebPush/selectors';
@@ -426,9 +437,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
426
437
  channel = '';
427
438
  }
428
439
  this.setState({ channel, activeMode });
429
- // Always reset archive mode and selection when mounting a new channel
430
- this.props.actions.setArchivedMode(false);
431
- this.props.actions.clearTemplateSelection();
432
440
  // Clear templates when entering account selection mode to prevent showing old channel templates
433
441
  if (activeMode === ACCOUNT_SELECTION_MODE) {
434
442
  this.props.actions.resetTemplate();
@@ -819,41 +827,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
819
827
  this.getAllTemplates({params, resetPage: true});
820
828
  }
821
829
 
822
- // Archive / Unarchive / Bulk Archive / Bulk Unarchive — detect completion and refresh listing
823
- const wasArchiving = this.props.Templates.archiveInProgress;
824
- const isArchiveDone = !nextProps.Templates.archiveInProgress && wasArchiving;
825
- if (isArchiveDone && !nextProps.Templates.archiveError) {
826
- const { successMessage, description } = nextProps.Templates.archiveSuccessPayload || {};
827
- if (successMessage) CapNotification.success({ message: successMessage, description });
828
- this.getAllTemplates({ params, resetPage: true });
829
- }
830
-
831
- const wasUnarchiving = this.props.Templates.unarchiveInProgress;
832
- const isUnarchiveDone = !nextProps.Templates.unarchiveInProgress && wasUnarchiving;
833
- if (isUnarchiveDone && !nextProps.Templates.unarchiveError) {
834
- const { successMessage, description } = nextProps.Templates.unarchiveSuccessPayload || {};
835
- if (successMessage) CapNotification.success({ message: successMessage, description });
836
- this.getAllTemplates({ params, resetPage: true });
837
- }
838
-
839
- const wasBulkArchiving = this.props.Templates.bulkArchiveInProgress;
840
- const isBulkArchiveDone = !nextProps.Templates.bulkArchiveInProgress && wasBulkArchiving;
841
- if (isBulkArchiveDone && !nextProps.Templates.bulkArchiveError) {
842
- const { successMessage, count } = nextProps.Templates.bulkArchiveSuccessPayload || {};
843
- const msg = successMessage ? successMessage(count) : `${count} templates archived successfully`;
844
- CapNotification.success({ message: msg });
845
- this.getAllTemplates({ params, resetPage: true });
846
- }
847
-
848
- const wasBulkUnarchiving = this.props.Templates.bulkUnarchiveInProgress;
849
- const isBulkUnarchiveDone = !nextProps.Templates.bulkUnarchiveInProgress && wasBulkUnarchiving;
850
- if (isBulkUnarchiveDone && !nextProps.Templates.bulkUnarchiveError) {
851
- const { successMessage, count } = nextProps.Templates.bulkUnarchiveSuccessPayload || {};
852
- const msg = successMessage ? successMessage(count) : `${count} templates unarchived successfully`;
853
- CapNotification.success({ message: msg });
854
- this.getAllTemplates({ params, resetPage: true });
855
- }
856
-
857
830
  if (!nextProps.Templates.sendingFile && !isEqual(this.props.Templates.sendingFile, nextProps.Templates.sendingFile) && !nextProps.Templates.errorSendingFile) {
858
831
  const module = this.props.location.query.module ? this.props.location.query.module : 'default';
859
832
  const isLanguageSupport = (this.props.location.query.isLanguageSupport) ? this.props.location.query.isLanguageSupport : true;
@@ -1622,11 +1595,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1622
1595
  let queryParams = params || {};
1623
1596
  let page = this.state.page;
1624
1597
  const { activeMode } = this.state;
1625
- // Archive filter — use explicit param if provided (props may not have updated yet due to async dispatch)
1626
- if (!queryParams.archiveStatus) {
1627
- const archiveFilter = get(this.props, 'Templates.archiveFilter', 'active');
1628
- queryParams.archiveStatus = archiveFilter;
1629
- }
1630
1598
  if (activeMode === ACCOUNT_SELECTION_MODE) {
1631
1599
  this.setTemplatesMode();
1632
1600
  }
@@ -1893,9 +1861,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1893
1861
  const currentChannel = channel.toUpperCase();
1894
1862
  const {channel: stateChannel} = this.state;
1895
1863
  const channelLowerCase = stateChannel.toLowerCase();
1896
- const _selectedIds = get(this.props, 'Templates.selectedTemplateIds', []);
1897
- const _selectedIdsArray = _selectedIds && typeof _selectedIds.toJS === 'function' ? _selectedIds.toJS() : (Array.isArray(_selectedIds) ? _selectedIds : []);
1898
- const hasSelection = this.props.isFullMode && _selectedIdsArray.length > 0;
1899
1864
  let filteredTemplates = templates;
1900
1865
  let isTraiDltFeature = false;
1901
1866
  switch (currentChannel) {
@@ -1935,65 +1900,47 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1935
1900
  const iosBodyType = get(template, 'versions.base.content.IOS.bodyType');
1936
1901
  const inappBodyType = androidBodyType || iosBodyType;
1937
1902
  const isZaloPreviewLoading = previewTemplateId === template?._id;
1938
- const selectedIdsForCard = get(this.props, 'Templates.selectedTemplateIds', []);
1939
- const selectedIdsArrayForCard = selectedIdsForCard.toJS ? selectedIdsForCard.toJS() : selectedIdsForCard;
1940
- // Archive eligibility per template: Zalo never; WhatsApp/RCS not when pending/awaiting
1941
- const cardWhatsappStatus = get(template, `versions.base.content.${WHATSAPP_LOWERCASE}.status`, '');
1942
- const cardRcsStatus = get(template, 'versions.base.content.RCS.rcsContent.cardContent[0].Status', '');
1943
- const isCardArchiveEligible = this.isChannelArchiveEligible(currentChannel, cardWhatsappStatus, cardRcsStatus);
1944
- const isArchivedMode = get(this.props, 'Templates.isArchivedMode', false);
1945
- const isAnyArchiveInProgress = !!(get(this.props, 'Templates.archiveInProgress') || get(this.props, 'Templates.unarchiveInProgress') || get(this.props, 'Templates.bulkArchiveInProgress') || get(this.props, 'Templates.bulkUnarchiveInProgress'));
1946
1903
  const templateData = {
1947
1904
  key: `${currentChannel}-card-${template?.name}`,
1948
1905
  title: (
1949
- <span className="template-card-title">
1950
- {this.props.isFullMode && this.props.location.query.type !== EMBEDDED && isCardArchiveEligible && (
1951
- <CapCheckbox
1952
- checked={selectedIdsArrayForCard.includes(template._id)}
1953
- onChange={() => !isAnyArchiveInProgress && this.props.actions.toggleTemplateSelection(template._id)}
1954
- onClick={(e) => e.stopPropagation()}
1955
- disabled={isAnyArchiveInProgress}
1956
- />
1906
+ <span title={template?.name}>
1907
+ {template?.name}
1908
+ {currentChannel === INAPP && (
1909
+ <CapRow>
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>
1957
1921
  )}
1958
- <CapLabel.CapLabelInline type="label1" title={template?.name} className="template-card-name">
1959
- {template?.name}
1960
- {currentChannel === INAPP && (
1961
- <CapRow>
1962
- <CapColoredTag
1963
- tagColor={INAPP_LAYOUT_DETAILS[inappBodyType]?.tagColor}
1964
- tagTextColor={
1965
- INAPP_LAYOUT_DETAILS[inappBodyType]?.tagTextColor
1966
- }
1967
- tagHeight="1.25rem"
1968
- tagFontSize="12px"
1969
- >
1970
- {INAPP_LAYOUT_DETAILS[inappBodyType]?.text}
1971
- </CapColoredTag>
1972
- </CapRow>
1973
- )}
1974
- </CapLabel.CapLabelInline>
1975
1922
  </span>
1976
1923
  ),
1977
- extra: isArchivedMode ? [] : [
1924
+ extra: [
1978
1925
  // Hide preview icon for channels that support Test and Preview
1979
1926
  // Show preview icon only for channels that don't support Test and Preview
1980
1927
  (() => {
1981
1928
  // Channels that have Test and Preview integrated
1982
1929
  const testAndPreviewChannels = [EMAIL, SMS, WHATSAPP, RCS, INAPP, MOBILE_PUSH, VIBER, ZALO];
1983
1930
  const isTestAndPreviewSupported = testAndPreviewChannels.includes(currentChannel.toUpperCase());
1984
-
1931
+
1985
1932
  // Don't show preview icon if channel supports Test and Preview
1986
1933
  if (isTestAndPreviewSupported) {
1987
1934
  return null;
1988
1935
  }
1989
-
1936
+
1990
1937
  // Show preview icon for other channels (e.g., WeChat, Line, Facebook, Ebill)
1991
1938
  if (currentChannel === ZALO && isZaloPreviewLoading) {
1992
1939
  return (
1993
1940
  <CapSpin style={{ marginRight: "16px", position: "static", display: "inline" }} spinning />
1994
1941
  );
1995
1942
  }
1996
-
1943
+
1997
1944
  return this.getHoverComponent(
1998
1945
  <CapIcon
1999
1946
  className={`view-${channelLowerCase}`}
@@ -2013,7 +1960,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2013
1960
  );
2014
1961
  })()
2015
1962
  ],
2016
- hoverOption: isArchivedMode ? null : (
1963
+ hoverOption: (
2017
1964
  <CapButton
2018
1965
  className={
2019
1966
  this.props.isFullMode
@@ -2053,8 +2000,8 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2053
2000
  <CapDropdown
2054
2001
  overlay={
2055
2002
  <CapMenu>
2056
- {/* Phase 16: Test and Preview menu item - Show for supported channels, hide in archived mode */}
2057
- {!isArchivedMode && (this.isTestAndPreviewSupported() ||
2003
+ {/* Phase 16: Test and Preview menu item - Show for supported channels */}
2004
+ {(this.isTestAndPreviewSupported() ||
2058
2005
  (this.state.channel.toUpperCase() === WHATSAPP &&
2059
2006
  status === WHATSAPP_STATUSES.approved) || (this.state.channel.toUpperCase() === RCS &&
2060
2007
  rcsStatus === RCS_STATUSES.approved)) && (
@@ -2077,7 +2024,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2077
2024
  {![WECHAT, WHATSAPP, ZALO].includes(
2078
2025
  this.state.channel.toUpperCase()
2079
2026
  ) &&
2080
- !isTraiDltFeature && !template.isArchived && (
2027
+ !isTraiDltFeature && (
2081
2028
  <CapMenu.Item
2082
2029
  className={`duplicate-${channelLowerCase}`}
2083
2030
  onClick={() => this.duplicateTemplate(template)}
@@ -2085,26 +2032,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2085
2032
  <FormattedMessage {...messages.duplicateButton} />
2086
2033
  </CapMenu.Item>
2087
2034
  )}
2088
- {/* Archive/Unarchive menu item (full mode only, not for Zalo, not for WhatsApp/RCS awaiting/pending) */}
2089
- {(() => {
2090
- const channelUp = this.state.channel.toUpperCase();
2091
- if (!this.isChannelArchiveEligible(channelUp, status, rcsStatus)) return null;
2092
- return !template.isArchived ? (
2093
- <CapMenu.Item
2094
- className={`archive-${channelLowerCase}`}
2095
- onClick={() => this.handleTemplateArchiveAction({ templateId: template._id, templateName: template.name })}
2096
- >
2097
- <FormattedMessage {...messages.archiveButton} />
2098
- </CapMenu.Item>
2099
- ) : (
2100
- <CapMenu.Item
2101
- className={`unarchive-${channelLowerCase}`}
2102
- onClick={() => this.handleTemplateArchiveAction({ templateId: template._id, templateName: template.name, isUnarchive: true })}
2103
- >
2104
- <FormattedMessage {...messages.unarchiveButton} />
2105
- </CapMenu.Item>
2106
- );
2107
- })()}
2108
2035
  {/* Delete/Unmap menu item */}
2109
2036
  {(!(
2110
2037
  this.state.channel.toUpperCase() === WHATSAPP &&
@@ -2385,15 +2312,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2385
2312
  case WHATSAPP: {
2386
2313
  const { whatsappImageSrc = '', templateMsg, docPreview, whatsappVideoPreviewImg = '', templateHeaderPreview, templateFooterPreview, carouselData = [] } = getWhatsappContent(template);
2387
2314
  templateData.title = (
2388
- <CapRow type="flex" align="middle">
2389
- {this.props.isFullMode && this.props.location.query.type !== EMBEDDED && isCardArchiveEligible && (
2390
- <CapCheckbox
2391
- checked={selectedIdsArrayForCard.includes(template._id)}
2392
- onChange={() => !isAnyArchiveInProgress && this.props.actions.toggleTemplateSelection(template._id)}
2393
- onClick={(e) => e.stopPropagation()}
2394
- disabled={isAnyArchiveInProgress}
2395
- />
2396
- )}
2315
+ <CapRow>
2397
2316
  <CapLabel className="whatsapp-rcs-template-name">{template?.name}</CapLabel>
2398
2317
  <WhatsappStatusContainer template={template} />
2399
2318
  </CapRow>
@@ -2486,15 +2405,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2486
2405
  const name = get(template, "name", "");
2487
2406
  const statusDisplay=getRcsStatusType(status);
2488
2407
  templateData.title = (
2489
- <CapRow type="flex" align="middle">
2490
- {this.props.isFullMode && this.props.location.query.type !== EMBEDDED && isCardArchiveEligible && (
2491
- <CapCheckbox
2492
- checked={selectedIdsArrayForCard.includes(template._id)}
2493
- onChange={() => !isAnyArchiveInProgress && this.props.actions.toggleTemplateSelection(template._id)}
2494
- onClick={(e) => e.stopPropagation()}
2495
- disabled={isAnyArchiveInProgress}
2496
- />
2497
- )}
2408
+ <CapRow>
2498
2409
  <CapLabel className="whatsapp-rcs-template-name">{name}</CapLabel>
2499
2410
  <CapRow type="flex" align="middle" className="rcs-status-container zalo-status-color">
2500
2411
  <CapStatus
@@ -2618,7 +2529,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2618
2529
 
2619
2530
  //no templates available
2620
2531
  const showIllustrationForChannel = (forChannel) => {
2621
- return forChannel === channelLowerCase && isEmpty(templates) && isEmpty(this.state.searchText) && !isLoading && !get(this.props, 'Templates.isArchivedMode', false);
2532
+ return forChannel === channelLowerCase && isEmpty(templates) && isEmpty(this.state.searchText) && !isLoading;
2622
2533
  }
2623
2534
  //when filters applied not matching templates
2624
2535
  const filteredEmptyAndFullModeAs = (fullModeValue) => {
@@ -2636,11 +2547,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2636
2547
  const accountId = get(this.props, 'Templates.selectedWeChatAccount.uuid');
2637
2548
  const accounts = get(this.props, 'Templates.weCrmAccounts');
2638
2549
  const getAllTemplatesInProgress = get(this.props, 'Templates.getAllTemplatesInProgress');
2639
- const archiveListingRefreshType = get(this.props, 'Templates.archiveListingRefreshType', null);
2640
- const isArchiveOperationInProgress = get(this.props, 'Templates.archiveInProgress', false)
2641
- || get(this.props, 'Templates.unarchiveInProgress', false)
2642
- || get(this.props, 'Templates.bulkArchiveInProgress', false)
2643
- || get(this.props, 'Templates.bulkUnarchiveInProgress', false);
2644
2550
 
2645
2551
  const noWhatsappZaloTemplates = this.isFullMode() && isEmpty(templates) || !this.state.hostName;
2646
2552
  const noApprovedWhatsappZaloTemplates = filteredEmptyAndFullModeAs(false) && !isEmpty(this.state?.hostName);
@@ -2649,36 +2555,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2649
2555
 
2650
2556
  const noLoaderAndSearchText = isEmpty(this.state.searchText) && !isLoading;
2651
2557
 
2652
- const isArchivedModeLocal = get(this.props, 'Templates.isArchivedMode', false);
2653
- const selectedIdsLocal = get(this.props, 'Templates.selectedTemplateIds', []);
2654
- const selectedIdsArrayLocal = selectedIdsLocal && typeof selectedIdsLocal.toJS === 'function' ? selectedIdsLocal.toJS() : (Array.isArray(selectedIdsLocal) ? selectedIdsLocal : []);
2655
- const selectedCountLocal = selectedIdsArrayLocal.length;
2656
- const hasSelectionLocal = this.props.isFullMode && selectedCountLocal > 0;
2657
-
2658
- return (<div>
2558
+ return (<div>
2659
2559
  {[WECHAT, MOBILE_PUSH, WEBPUSH, INAPP, WHATSAPP, ZALO, RCS].includes(currentChannel) && this.showAccountName()}
2660
- <CapRow type="flex" align="middle" justify="space-between" className="filter-row">
2661
- <div className="filter-row-content">{filterContent}</div>
2662
- {hasSelectionLocal && (
2663
- <CapRow type="flex" align="middle" className="bulk-selection-bar">
2664
- <CapLabel type="label2">
2665
- {this.props.intl.formatMessage(messages.templatesSelected, { count: selectedCountLocal })}
2666
- </CapLabel>
2667
- <CapButton
2668
- type="primary"
2669
- prefix={<CapIcon type="archive" size="l" />}
2670
- onClick={() => this.handleBulkArchiveAction({ ids: selectedIdsArrayLocal, count: selectedCountLocal, isUnarchive: isArchivedModeLocal })}
2671
- >
2672
- <span className="archive-btn-label">
2673
- <FormattedMessage {...(isArchivedModeLocal ? messages.unarchiveButton : messages.archiveButton)} />
2674
- </span>
2675
- </CapButton>
2676
- <CapButton type="secondary" onClick={() => this.props.actions.clearTemplateSelection()}>
2677
- <FormattedMessage {...messages.archiveConfirmCancel} />
2678
- </CapButton>
2679
- </CapRow>
2680
- )}
2681
- </CapRow>
2560
+ {filterContent}
2682
2561
  {[WHATSAPP, ZALO, INAPP,RCS].includes(currentChannel) && this.selectedFilters()}
2683
2562
  {<div>
2684
2563
  {!isEmpty(filteredTemplates) || !isEmpty(this.state.searchText) || !isEmpty(this.props.Templates.templateError) ? (
@@ -2691,7 +2570,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2691
2570
  fbType={"list"}
2692
2571
  />
2693
2572
  </div>)
2694
- : [SMS_LOWERCASE, EMAIL_LOWERCASE].includes(this.state.channel.toLowerCase()) && !isLoading && !get(this.props, 'Templates.isArchivedMode', false) && this.getSmsEmailIllustration()
2573
+ : [SMS_LOWERCASE, EMAIL_LOWERCASE].includes(this.state.channel.toLowerCase()) && !isLoading && this.getSmsEmailIllustration()
2695
2574
  }
2696
2575
 
2697
2576
  {(this.state.selectedAccount === '' && isEmpty(this.props.Templates.selectedWeChatAccount)) && ([WECHAT_LOWERCASE, MOBILE_PUSH_LOWERCASE, INAPP_LOWERCASE].includes(this.state.channel.toLowerCase())) &&
@@ -2715,7 +2594,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2715
2594
  </div>
2716
2595
  )
2717
2596
  }
2718
- {(showWhatsappIllustration || showZaloIllustration) && !get(this.props, 'Templates.isArchivedMode', false) && (
2597
+ {(showWhatsappIllustration || showZaloIllustration) && (
2719
2598
  noLoaderAndSearchText &&
2720
2599
  <div style={this.isFullMode() ? { height: "calc(100vh - 325px)", overflow: 'auto' } : {}}>
2721
2600
  {noWhatsappZaloTemplates && <ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={currentChannel} hostName={this.state?.hostName}/>}
@@ -2756,27 +2635,8 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2756
2635
  <ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={currentChannel} hostName={this.state?.hostName}/>
2757
2636
  </div>
2758
2637
  }
2759
- {get(this.props, 'Templates.isArchivedMode', false) && isEmpty(templates) && !isLoading && !getAllTemplatesInProgress && isEmpty(this.state.searchText) && (
2760
- <CapRow className={this.isFullMode() ? 'illustration-scroll-wrapper' : ''}>
2761
- <ChannelTypeIllustration
2762
- isFullMode={this.props.isFullMode}
2763
- createTemplate={this.createTemplate}
2764
- currentChannel={currentChannel}
2765
- isArchivedMode
2766
- />
2767
- </CapRow>
2768
- )}
2769
2638
  {<CapCustomSkeleton loader={isInitialLoading && (isLoading || getAllTemplatesInProgress)} />}
2770
- {!isInitialLoading && getAllTemplatesInProgress && archiveListingRefreshType ? (
2771
- <div className="archive-listing-spinner">
2772
- <CapSpin spinning />
2773
- <CapLabel.CapLabelInline type="label1">
2774
- {archiveListingRefreshType === 'ARCHIVE' ? 'Archival in progress' : 'Unarchival in progress'}
2775
- </CapLabel.CapLabelInline>
2776
- </div>
2777
- ) : (
2778
- <CapPageSpinner spinning={!isInitialLoading && (isLoading || getAllTemplatesInProgress)} />
2779
- )}
2639
+ {<CapPageSpinner spinning={!isInitialLoading && (isLoading || getAllTemplatesInProgress)} />}
2780
2640
  </div>
2781
2641
  }
2782
2642
  </div>);
@@ -3418,10 +3278,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
3418
3278
  };
3419
3279
 
3420
3280
  handleEditClick(e, template, modeType, path, options) {
3421
- if (template && template.isArchived) {
3422
- CapNotification.error({ message: this.props.intl.formatMessage(messages.cannotEditArchivedTemplate) });
3423
- return;
3424
- }
3425
3281
  if (modeType && modeType !== undefined) {
3426
3282
  this.setState({modeType});
3427
3283
  }
@@ -3603,75 +3459,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
3603
3459
  this.setState({showModal: false});
3604
3460
  }
3605
3461
 
3606
- handleBulkArchiveAction = ({ ids, count, isUnarchive = false }) => {
3607
- const { intl, actions } = this.props;
3608
- const { channel } = this.state;
3609
- const title = isUnarchive
3610
- ? intl.formatMessage(messages.unarchiveTemplates)
3611
- : intl.formatMessage(messages.archiveTemplates);
3612
- const action = isUnarchive ? actions.bulkUnarchiveTemplates : actions.bulkArchiveTemplates;
3613
- const successMessage = (c) => intl.formatMessage(
3614
- isUnarchive ? messages.bulkUnarchiveSuccess : messages.bulkArchiveSuccess,
3615
- { count: c }
3616
- );
3617
- this.showArchiveConfirm({
3618
- title,
3619
- count,
3620
- isUnarchive,
3621
- onConfirm: () => action(channel, ids, successMessage),
3622
- });
3623
- };
3624
-
3625
- handleTemplateArchiveAction = ({ templateId, templateName, isUnarchive = false }) => {
3626
- const { intl, actions } = this.props;
3627
- const { channel } = this.state;
3628
- const title = isUnarchive
3629
- ? intl.formatMessage(messages.unarchiveTemplates)
3630
- : intl.formatMessage(messages.archiveTemplates);
3631
- const successMessage = isUnarchive
3632
- ? intl.formatMessage(messages.unarchiveTemplateSuccess)
3633
- : intl.formatMessage(messages.archiveTemplateSuccess);
3634
- const action = isUnarchive ? actions.unarchiveTemplate : actions.archiveTemplate;
3635
- this.showArchiveConfirm({
3636
- title,
3637
- count: 1,
3638
- isUnarchive,
3639
- onConfirm: () => action(
3640
- channel,
3641
- templateId,
3642
- successMessage,
3643
- buildTemplateNameDescription(intl.formatMessage(messages.templateNameLabel), templateName)
3644
- ),
3645
- });
3646
- };
3647
-
3648
- // Shared helper for archive/unarchive confirm modals:
3649
- // - no icon, lighter overlay, Confirm button on left (primary), Cancel on right
3650
- showArchiveConfirm = ({ title, content, onConfirm, count = 1, isUnarchive = false }) => {
3651
- const { intl } = this.props;
3652
- const confirmText = intl.formatMessage(messages.archiveConfirmOk);
3653
- const cancelText = intl.formatMessage(messages.archiveConfirmCancel);
3654
- // Derive content from count if not explicitly provided
3655
- const resolvedContent = content || (isUnarchive
3656
- ? intl.formatMessage(count > 1 ? messages.unarchiveTemplateContent : messages.unarchiveTemplateSingleContent)
3657
- : intl.formatMessage(count > 1 ? messages.archiveTemplateContent : messages.archiveTemplateSingleContent));
3658
- // AntD v3 footer order is [cancelButton][okButton]. Swap text+handler so
3659
- // "Confirm" (primary) appears on the left and "Cancel" (default) on the right.
3660
- CapModal.confirm({
3661
- title,
3662
- content: resolvedContent,
3663
- icon: ' ',
3664
- className: 'archive-confirm-modal',
3665
- maskStyle: { backgroundColor: 'rgba(0, 0, 0, 0.25)' },
3666
- cancelText: confirmText,
3667
- okText: cancelText,
3668
- cancelButtonProps: { type: 'primary' },
3669
- okButtonProps: { type: 'default' },
3670
- onCancel: (close) => { onConfirm(); close(); },
3671
- // onOk (the right "Cancel" button) just closes the modal — default behaviour
3672
- });
3673
- }
3674
-
3675
3462
  populateTemplatesList = (data, blankTemplateRequired, layoutSelection) => {
3676
3463
  if (!data) {
3677
3464
  return [];
@@ -3821,71 +3608,22 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
3821
3608
  deleteOption = this.props.intl.formatMessage(messages.unMapButton);
3822
3609
  }
3823
3610
  if (!layoutSelection) {
3824
- // Determine archive eligibility for this template:
3825
- // - Zalo: never eligible
3826
- // - WhatsApp: not eligible when status is awaitingApproval / pending / unsubmitted
3827
- // - RCS: not eligible when status is awaitingApproval / pending
3828
- const templateWhatsappStatus = get(template, `versions.base.content.${WHATSAPP_LOWERCASE}.status`, '');
3829
- const templateRcsStatus = get(template, 'versions.base.content.RCS.rcsContent.cardContent[0].Status', '');
3830
- const channelUpCase = this.state.channel.toUpperCase();
3831
- const isTemplateArchiveEligible = this.isChannelArchiveEligible(channelUpCase, templateWhatsappStatus, templateRcsStatus);
3832
-
3833
- // Checkbox on card header (full mode only, only for archive-eligible templates)
3834
- if (this.props.isFullMode && this.props.location.query.type !== EMBEDDED && isTemplateArchiveEligible) {
3835
- const selectedIds = get(this.props, 'Templates.selectedTemplateIds', []);
3836
- const selectedIdsArray = selectedIds.toJS ? selectedIds.toJS() : selectedIds;
3837
- temp.cardTop = (
3838
- <CapRow type="flex" align="middle" justify="space-between" className="template-card-top-bar">
3839
- <CapCheckbox
3840
- checked={selectedIdsArray.includes(template._id)}
3841
- onChange={() => this.props.actions.toggleTemplateSelection(template._id)}
3842
- onClick={(e) => e.stopPropagation()}
3843
- />
3844
- </CapRow>
3845
- );
3846
- }
3847
-
3848
3611
  temp.footer = (
3849
3612
  <div className="footer-container">
3850
3613
  <div className="card-title">
3851
3614
  <span className="template-name" style={{ fontWeight: `${this.state.channel.toLowerCase() === 'wechat' ? '400' : '600'}` }}>
3852
3615
  { 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>}
3853
3616
  {template?.name}
3854
- {template.isArchived && <CapColoredTag tagColor={CAP_G08} tagTextColor={CAP_G05} className="archived-tag">{this.props.intl.formatMessage(messages.archivedTag)}</CapColoredTag>}
3855
3617
  </span>
3856
- {this.props.location.query.type !== EMBEDDED && <CapPopover
3618
+ {this.props.location.query.type !== 'embedded' && <CapPopover
3857
3619
  trigger="click"
3858
3620
  content={
3859
3621
  <div className="popover-content">
3860
- {this.state.channel !== 'wechat' && !template.isArchived && <div className="popover-action-container">
3861
- <CapButton type="link" onClick={() => this.duplicateTemplate(template)} className="popover-action">
3862
- {this.props.intl.formatMessage(messages.duplicateButton)}
3863
- </CapButton>
3864
- </div>}
3865
- {this.props.isFullMode && isTemplateArchiveEligible && !template.isArchived && <div className="popover-action-container">
3866
- <CapButton
3867
- type="link"
3868
- onClick={() => this.handleTemplateArchiveAction({ templateId: template._id, templateName: template.name })}
3869
- className="popover-action popover-archive-action"
3870
- >
3871
- <CapIcon type="archive" size="s" />
3872
- <CapLabel.CapLabelInline type="label1">{this.props.intl.formatMessage(messages.archiveButton)}</CapLabel.CapLabelInline>
3873
- </CapButton>
3874
- </div>}
3875
- {this.props.isFullMode && isTemplateArchiveEligible && template.isArchived && <div className="popover-action-container">
3876
- <CapButton
3877
- type="link"
3878
- onClick={() => this.handleTemplateArchiveAction({ templateId: template._id, templateName: template.name, isUnarchive: true })}
3879
- className="popover-action popover-archive-action"
3880
- >
3881
- <CapIcon type="archive" size="s" />
3882
- <CapLabel.CapLabelInline type="label1">{this.props.intl.formatMessage(messages.unarchiveButton)}</CapLabel.CapLabelInline>
3883
- </CapButton>
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>
3884
3624
  </div>}
3885
3625
  <div className="popover-action-container">
3886
- <CapButton type="link" onClick={() => this.toggleDeleteTemplateModal(template)} className="popover-action">
3887
- {deleteOption}
3888
- </CapButton>
3626
+ <span onClick={() => this.toggleDeleteTemplateModal(template)} className="popover-action" style={{cursor: 'pointer', padding: '8px 0px'}}>{deleteOption}</span>
3889
3627
  </div>
3890
3628
  </div>
3891
3629
  }
@@ -3938,27 +3676,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
3938
3676
  return false;
3939
3677
  }
3940
3678
  }
3941
- isFullMode = () => this.props.location.query.type !== EMBEDDED || this.props.isFullMode
3942
-
3943
- isChannelArchiveEligible = (channel, whatsappStatus, rcsStatus) => (
3944
- channel !== ZALO &&
3945
- !(channel === WHATSAPP && [WHATSAPP_STATUSES.awaitingApproval, WHATSAPP_STATUSES.pending, WHATSAPP_STATUSES.unsubmitted].includes(whatsappStatus)) &&
3946
- !(channel === RCS && [RCS_STATUSES.awaitingApproval, RCS_STATUSES.pending].includes(rcsStatus))
3947
- )
3948
-
3949
- enterArchivedMode = () => {
3950
- this.props.actions.setArchivedMode(true);
3951
- this.setState({ searchText: '', page: 1 }, () => {
3952
- this.getAllTemplates({ params: { name: '', sortBy: this.state.sortBy, archiveStatus: 'archived' }, resetPage: true }, true);
3953
- });
3954
- }
3955
-
3956
- exitArchivedMode = () => {
3957
- this.props.actions.setArchivedMode(false);
3958
- this.setState({ searchText: '', page: 1 }, () => {
3959
- this.getAllTemplates({ params: { name: '', sortBy: this.state.sortBy, archiveStatus: 'active' }, resetPage: true }, true);
3960
- });
3961
- }
3679
+ isFullMode = () => this.props.location.query.type !== "embedded" || this.props.isFullMode
3962
3680
  isCreateDisabled = () => {
3963
3681
  let isDisabled = this.isLoading();
3964
3682
  const channel = this.state.channel.toUpperCase();
@@ -4496,16 +4214,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4496
4214
  if (([WHATSAPP_LOWERCASE, ZALO_LOWERCASE, RCS_LOWERCASE].includes(this.state?.channel?.toLocaleLowerCase()) && isEmpty(this.state?.hostName))) {
4497
4215
  isfilterContentVisisble = false;
4498
4216
  }
4499
- const _isArchivedMode = get(this.props, 'Templates.isArchivedMode', false);
4500
- const _renderSelectedIds = get(this.props, 'Templates.selectedTemplateIds', []);
4501
- const _renderSelectedIdsArray = _renderSelectedIds && typeof _renderSelectedIds.toJS === 'function' ? _renderSelectedIds.toJS() : (Array.isArray(_renderSelectedIds) ? _renderSelectedIds : []);
4502
- const _renderHasSelection = this.props.isFullMode && _renderSelectedIdsArray.length > 0;
4503
-
4504
4217
  const filterContent = (( isfilterContentVisisble || [WECHAT, MOBILE_PUSH, INAPP].includes(this.state.channel.toUpperCase())) && <div className="action-container">
4505
4218
  {isfilterContentVisisble && <CapInput.Search
4506
4219
  className="search-text"
4507
4220
  style={{width: '210px'}}
4508
- placeholder={_isArchivedMode ? this.props.intl.formatMessage(messages.searchArchivedTemplates) : this.props.intl.formatMessage(messages.searchText)}
4221
+ placeholder={this.props.intl.formatMessage(messages.searchText)}
4509
4222
  value={this.state.searchText}
4510
4223
  onChange={(e) => this.searchTemplate(e.target.value, this.state.channel)}
4511
4224
  disabled={this.checkSearchDisabled()}
@@ -4657,9 +4370,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4657
4370
  </div>
4658
4371
  )
4659
4372
  }
4660
- <div className="template-listing-header-actions">
4661
- {!_isArchivedMode && !_renderHasSelection && (
4662
- this.state?.channel?.toLowerCase() === WHATSAPP_LOWERCASE && (isWhatsappCountExeeded) ? (
4373
+ <div style={{display: "flex", justifyContent: "space-between", alignItems: 'center'}}>
4374
+ {
4375
+ this.state?.channel?.toLowerCase() === WHATSAPP_LOWERCASE && (isWhatsappCountExeeded)? (
4663
4376
  <CapTooltip title={whatsappCountExceedText}>
4664
4377
  <div className="button-disabled-tooltip-wrapper">
4665
4378
  {createButton}
@@ -4667,29 +4380,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4667
4380
  </CapTooltip>
4668
4381
  )
4669
4382
  : isfilterContentVisisble && !isWechatEmbedded && !this.props.isDltFromRcs && createButton
4670
- )}
4671
- {/* More (⋯) menu: full mode only, not archived mode, not Zalo (no archive support), not when selection active */}
4672
- {!_isArchivedMode && !_renderHasSelection && this.props.isFullMode && this.props.location.query.type !== EMBEDDED && channelLowerCase !== ZALO_LOWERCASE && (
4673
- <CapDropdown
4674
- trigger={['click']}
4675
- overlay={
4676
- <CapMenu>
4677
- <CapMenu.Item
4678
- key="archived"
4679
- onClick={this.enterArchivedMode}
4680
- >
4681
- <FormattedMessage {...messages.archivedTemplates} />
4682
- </CapMenu.Item>
4683
- </CapMenu>
4684
- }
4685
- placement="bottomRight"
4686
- >
4687
- <CapButton type="flat" className="template-listing-more-btn">
4688
- <CapIcon type="more" />
4689
- </CapButton>
4690
- </CapDropdown>
4691
- )}
4383
+ }
4692
4384
  </div>
4385
+
4693
4386
  </div>);
4694
4387
  let htmlPreviewContent = "";
4695
4388
  if (this.state.channel.toLowerCase() === 'ebill') {
@@ -4727,7 +4420,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4727
4420
  className={`creatives-templates-list ${
4728
4421
  this.props.isFullMode ? "full-mode" : "library-mode"
4729
4422
  }`}
4730
- style={{ position: 'relative' }}
4731
4423
  >
4732
4424
  <input
4733
4425
  type="file"
@@ -4739,18 +4431,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4739
4431
  }
4740
4432
  />
4741
4433
 
4742
- {/* Archived mode header with back arrow (full mode only) */}
4743
- {this.props.isFullMode && get(this.props, 'Templates.isArchivedMode', false) && (
4744
- <CapRow type="flex" align="middle" className="archived-mode-header">
4745
- <CapIcon
4746
- type="back"
4747
- className="archived-mode-back-icon"
4748
- onClick={this.exitArchivedMode}
4749
- />
4750
- <CapHeading type="h3"><FormattedMessage {...messages.archivedTemplates} /></CapHeading>
4751
- </CapRow>
4752
- )}
4753
-
4754
4434
  {channel.toLowerCase() === WHATSAPP_LOWERCASE &&
4755
4435
  showWhatsappCountWarning ? (
4756
4436
  <CapAlert message={whatsappCountExceedText} type="info" />