@capillarytech/creatives-library 8.0.345 → 8.0.346

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 (30) 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/v2Components/CapCustomSkeleton/index.js +1 -1
  5. package/v2Components/CapCustomSkeleton/tests/__snapshots__/index.test.js.snap +12 -12
  6. package/v2Containers/CreativesContainer/SlideBoxFooter.js +1 -3
  7. package/v2Containers/CreativesContainer/index.js +0 -5
  8. package/v2Containers/CreativesContainer/messages.js +0 -4
  9. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +0 -3
  10. package/v2Containers/Email/reducer.js +12 -3
  11. package/v2Containers/Email/sagas.js +9 -4
  12. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +4 -0
  13. package/v2Containers/Email/tests/reducer.test.js +47 -0
  14. package/v2Containers/Email/tests/sagas.test.js +146 -6
  15. package/v2Containers/Templates/ChannelTypeIllustration.js +6 -23
  16. package/v2Containers/Templates/_templates.scss +24 -130
  17. package/v2Containers/Templates/actions.js +0 -36
  18. package/v2Containers/Templates/constants.js +0 -23
  19. package/v2Containers/Templates/index.js +30 -286
  20. package/v2Containers/Templates/messages.js +0 -68
  21. package/v2Containers/Templates/reducer.js +0 -68
  22. package/v2Containers/Templates/sagas.js +1 -89
  23. package/v2Containers/Templates/selectors.js +0 -12
  24. package/v2Containers/Templates/tests/ChannelTypeIllustration.test.js +0 -12
  25. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1122 -1300
  26. package/v2Containers/Templates/tests/index.test.js +0 -6
  27. package/v2Containers/Templates/tests/reducer.test.js +0 -178
  28. package/v2Containers/Templates/tests/sagas.test.js +8 -314
  29. package/v2Containers/Templates/tests/selector.test.js +0 -32
  30. package/v2Containers/Assets/images/archive_Empty_Illustration.svg +0 -9
@@ -50,7 +50,9 @@ import {
50
50
  CapStatus,
51
51
  CapColoredTag,
52
52
  CapSpin,
53
- CapCheckbox,
53
+ CapCard,
54
+ CapColumn,
55
+ CapCarousel
54
56
  } from "@capillarytech/cap-ui-library";
55
57
  import { makeSelectTemplates, makeSelectTemplatesResponse } from './selectors';
56
58
  import { makeSelectCreate as makeSelectCreateSms } from '../Sms/Create/selectors';
@@ -114,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();
@@ -1587,11 +1595,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1587
1595
  let queryParams = params || {};
1588
1596
  let page = this.state.page;
1589
1597
  const { activeMode } = this.state;
1590
- // Archive filter — use explicit param if provided (props may not have updated yet due to async dispatch)
1591
- if (!queryParams.archiveStatus) {
1592
- const archiveFilter = get(this.props, 'Templates.archiveFilter', 'active');
1593
- queryParams.archiveStatus = archiveFilter;
1594
- }
1595
1598
  if (activeMode === ACCOUNT_SELECTION_MODE) {
1596
1599
  this.setTemplatesMode();
1597
1600
  }
@@ -1858,9 +1861,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1858
1861
  const currentChannel = channel.toUpperCase();
1859
1862
  const {channel: stateChannel} = this.state;
1860
1863
  const channelLowerCase = stateChannel.toLowerCase();
1861
- const _selectedIds = get(this.props, 'Templates.selectedTemplateIds', []);
1862
- const _selectedIdsArray = _selectedIds && typeof _selectedIds.toJS === 'function' ? _selectedIds.toJS() : (Array.isArray(_selectedIds) ? _selectedIds : []);
1863
- const hasSelection = this.props.isFullMode && _selectedIdsArray.length > 0;
1864
1864
  let filteredTemplates = templates;
1865
1865
  let isTraiDltFeature = false;
1866
1866
  switch (currentChannel) {
@@ -1900,26 +1900,10 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1900
1900
  const iosBodyType = get(template, 'versions.base.content.IOS.bodyType');
1901
1901
  const inappBodyType = androidBodyType || iosBodyType;
1902
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));
1911
1903
  const templateData = {
1912
1904
  key: `${currentChannel}-card-${template?.name}`,
1913
1905
  title: (
1914
- <span title={template?.name} style={{ display: 'flex', alignItems: 'center' }}>
1915
- {this.props.isFullMode && this.props.location.query.type !== 'embedded' && isCardArchiveEligible && (
1916
- <CapCheckbox
1917
- checked={selectedIdsArrayForCard.includes(template._id)}
1918
- onChange={() => this.props.actions.toggleTemplateSelection(template._id)}
1919
- onClick={(e) => e.stopPropagation()}
1920
- style={{ marginRight: CAP_SPACE_08, flexShrink: 0 }}
1921
- />
1922
- )}
1906
+ <span title={template?.name}>
1923
1907
  {template?.name}
1924
1908
  {currentChannel === INAPP && (
1925
1909
  <CapRow>
@@ -2040,7 +2024,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2040
2024
  {![WECHAT, WHATSAPP, ZALO].includes(
2041
2025
  this.state.channel.toUpperCase()
2042
2026
  ) &&
2043
- !isTraiDltFeature && !template.isArchived && (
2027
+ !isTraiDltFeature && (
2044
2028
  <CapMenu.Item
2045
2029
  className={`duplicate-${channelLowerCase}`}
2046
2030
  onClick={() => this.duplicateTemplate(template)}
@@ -2048,42 +2032,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2048
2032
  <FormattedMessage {...messages.duplicateButton} />
2049
2033
  </CapMenu.Item>
2050
2034
  )}
2051
- {/* Archive/Unarchive menu item (full mode only, not for Zalo, not for WhatsApp/RCS awaiting/pending) */}
2052
- {(() => {
2053
- const channelUp = this.state.channel.toUpperCase();
2054
- const isArchiveEligible = channelUp !== ZALO &&
2055
- !(channelUp === WHATSAPP && [WHATSAPP_STATUSES.awaitingApproval, WHATSAPP_STATUSES.pending, WHATSAPP_STATUSES.unsubmitted].includes(status)) &&
2056
- !(channelUp === RCS && [RCS_STATUSES.awaitingApproval, RCS_STATUSES.pending].includes(rcsStatus));
2057
- if (!isArchiveEligible) return null;
2058
- return !template.isArchived ? (
2059
- <CapMenu.Item
2060
- className={`archive-${channelLowerCase}`}
2061
- onClick={() => {
2062
- this.showArchiveConfirm({
2063
- title: this.props.intl.formatMessage(messages.archiveTemplates),
2064
- onConfirm: () => this.props.actions.archiveTemplate(this.state.channel, template._id, template.name),
2065
- count: 1,
2066
- });
2067
- }}
2068
- >
2069
- <FormattedMessage {...messages.archiveButton} />
2070
- </CapMenu.Item>
2071
- ) : (
2072
- <CapMenu.Item
2073
- className={`unarchive-${channelLowerCase}`}
2074
- onClick={() => {
2075
- this.showArchiveConfirm({
2076
- title: this.props.intl.formatMessage(messages.unarchiveTemplates),
2077
- onConfirm: () => this.props.actions.unarchiveTemplate(this.state.channel, template._id, template.name),
2078
- count: 1,
2079
- isUnarchive: true,
2080
- });
2081
- }}
2082
- >
2083
- <FormattedMessage {...messages.unarchiveButton} />
2084
- </CapMenu.Item>
2085
- );
2086
- })()}
2087
2035
  {/* Delete/Unmap menu item */}
2088
2036
  {(!(
2089
2037
  this.state.channel.toUpperCase() === WHATSAPP &&
@@ -2364,15 +2312,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2364
2312
  case WHATSAPP: {
2365
2313
  const { whatsappImageSrc = '', templateMsg, docPreview, whatsappVideoPreviewImg = '', templateHeaderPreview, templateFooterPreview, carouselData = [] } = getWhatsappContent(template);
2366
2314
  templateData.title = (
2367
- <CapRow type="flex" align="middle">
2368
- {this.props.isFullMode && this.props.location.query.type !== 'embedded' && isCardArchiveEligible && (
2369
- <CapCheckbox
2370
- checked={selectedIdsArrayForCard.includes(template._id)}
2371
- onChange={() => this.props.actions.toggleTemplateSelection(template._id)}
2372
- onClick={(e) => e.stopPropagation()}
2373
- style={{ marginRight: CAP_SPACE_08, flexShrink: 0 }}
2374
- />
2375
- )}
2315
+ <CapRow>
2376
2316
  <CapLabel className="whatsapp-rcs-template-name">{template?.name}</CapLabel>
2377
2317
  <WhatsappStatusContainer template={template} />
2378
2318
  </CapRow>
@@ -2465,15 +2405,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2465
2405
  const name = get(template, "name", "");
2466
2406
  const statusDisplay=getRcsStatusType(status);
2467
2407
  templateData.title = (
2468
- <CapRow type="flex" align="middle">
2469
- {this.props.isFullMode && this.props.location.query.type !== 'embedded' && isCardArchiveEligible && (
2470
- <CapCheckbox
2471
- checked={selectedIdsArrayForCard.includes(template._id)}
2472
- onChange={() => this.props.actions.toggleTemplateSelection(template._id)}
2473
- onClick={(e) => e.stopPropagation()}
2474
- style={{ marginRight: CAP_SPACE_08, flexShrink: 0 }}
2475
- />
2476
- )}
2408
+ <CapRow>
2477
2409
  <CapLabel className="whatsapp-rcs-template-name">{name}</CapLabel>
2478
2410
  <CapRow type="flex" align="middle" className="rcs-status-container zalo-status-color">
2479
2411
  <CapStatus
@@ -2597,7 +2529,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2597
2529
 
2598
2530
  //no templates available
2599
2531
  const showIllustrationForChannel = (forChannel) => {
2600
- 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;
2601
2533
  }
2602
2534
  //when filters applied not matching templates
2603
2535
  const filteredEmptyAndFullModeAs = (fullModeValue) => {
@@ -2623,49 +2555,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2623
2555
 
2624
2556
  const noLoaderAndSearchText = isEmpty(this.state.searchText) && !isLoading;
2625
2557
 
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>
2558
+ return (<div>
2633
2559
  {[WECHAT, MOBILE_PUSH, WEBPUSH, INAPP, WHATSAPP, ZALO, RCS].includes(currentChannel) && this.showAccountName()}
2634
- <CapRow type="flex" align="middle" justify="space-between" className="filter-row">
2635
- <div className="filter-row-content">{filterContent}</div>
2636
- {hasSelectionLocal && (
2637
- <CapRow type="flex" align="middle" className="bulk-selection-bar">
2638
- <CapLabel type="label2">
2639
- {this.props.intl.formatMessage(messages.templatesSelected, { count: selectedCountLocal })}
2640
- </CapLabel>
2641
- <CapButton
2642
- type="primary"
2643
- prefix={<CapIcon type="archive" size="l" />}
2644
- onClick={() => {
2645
- this.showArchiveConfirm({
2646
- title: this.props.intl.formatMessage(isArchivedModeLocal ? messages.unarchiveTemplates : messages.archiveTemplates),
2647
- onConfirm: () => {
2648
- if (isArchivedModeLocal) {
2649
- this.props.actions.bulkUnarchiveTemplates(this.state.channel, selectedIdsArrayLocal);
2650
- } else {
2651
- this.props.actions.bulkArchiveTemplates(this.state.channel, selectedIdsArrayLocal);
2652
- }
2653
- },
2654
- count: selectedCountLocal,
2655
- isUnarchive: isArchivedModeLocal,
2656
- });
2657
- }}
2658
- >
2659
- <span className="archive-btn-label">
2660
- <FormattedMessage {...(isArchivedModeLocal ? messages.unarchiveButton : messages.archiveButton)} />
2661
- </span>
2662
- </CapButton>
2663
- <CapButton type="secondary" onClick={() => this.props.actions.clearTemplateSelection()}>
2664
- <FormattedMessage {...messages.archiveConfirmCancel} />
2665
- </CapButton>
2666
- </CapRow>
2667
- )}
2668
- </CapRow>
2560
+ {filterContent}
2669
2561
  {[WHATSAPP, ZALO, INAPP,RCS].includes(currentChannel) && this.selectedFilters()}
2670
2562
  {<div>
2671
2563
  {!isEmpty(filteredTemplates) || !isEmpty(this.state.searchText) || !isEmpty(this.props.Templates.templateError) ? (
@@ -2678,7 +2570,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2678
2570
  fbType={"list"}
2679
2571
  />
2680
2572
  </div>)
2681
- : [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()
2682
2574
  }
2683
2575
 
2684
2576
  {(this.state.selectedAccount === '' && isEmpty(this.props.Templates.selectedWeChatAccount)) && ([WECHAT_LOWERCASE, MOBILE_PUSH_LOWERCASE, INAPP_LOWERCASE].includes(this.state.channel.toLowerCase())) &&
@@ -2702,7 +2594,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2702
2594
  </div>
2703
2595
  )
2704
2596
  }
2705
- {(showWhatsappIllustration || showZaloIllustration) && !get(this.props, 'Templates.isArchivedMode', false) && (
2597
+ {(showWhatsappIllustration || showZaloIllustration) && (
2706
2598
  noLoaderAndSearchText &&
2707
2599
  <div style={this.isFullMode() ? { height: "calc(100vh - 325px)", overflow: 'auto' } : {}}>
2708
2600
  {noWhatsappZaloTemplates && <ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={currentChannel} hostName={this.state?.hostName}/>}
@@ -2743,16 +2635,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2743
2635
  <ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={currentChannel} hostName={this.state?.hostName}/>
2744
2636
  </div>
2745
2637
  }
2746
- {get(this.props, 'Templates.isArchivedMode', false) && isEmpty(templates) && !isLoading && !getAllTemplatesInProgress && isEmpty(this.state.searchText) && (
2747
- <div className={this.isFullMode() ? 'illustration-scroll-wrapper' : ''}>
2748
- <ChannelTypeIllustration
2749
- isFullMode={this.props.isFullMode}
2750
- createTemplate={this.createTemplate}
2751
- currentChannel={currentChannel}
2752
- isArchivedMode
2753
- />
2754
- </div>
2755
- )}
2756
2638
  {<CapCustomSkeleton loader={isInitialLoading && (isLoading || getAllTemplatesInProgress)} />}
2757
2639
  {<CapPageSpinner spinning={!isInitialLoading && (isLoading || getAllTemplatesInProgress)} />}
2758
2640
  </div>
@@ -3396,10 +3278,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
3396
3278
  };
3397
3279
 
3398
3280
  handleEditClick(e, template, modeType, path, options) {
3399
- if (template && template.isArchived) {
3400
- CapNotification.error({ message: this.props.intl.formatMessage(messages.cannotEditArchivedTemplate) });
3401
- return;
3402
- }
3403
3281
  if (modeType && modeType !== undefined) {
3404
3282
  this.setState({modeType});
3405
3283
  }
@@ -3581,33 +3459,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
3581
3459
  this.setState({showModal: false});
3582
3460
  }
3583
3461
 
3584
- // Shared helper for archive/unarchive confirm modals:
3585
- // - no icon, lighter overlay, Confirm button on left (primary), Cancel on right
3586
- showArchiveConfirm = ({ title, content, onConfirm, count = 1, isUnarchive = false }) => {
3587
- const { intl } = this.props;
3588
- const confirmText = intl.formatMessage(messages.archiveConfirmOk);
3589
- const cancelText = intl.formatMessage(messages.archiveConfirmCancel);
3590
- // Derive content from count if not explicitly provided
3591
- const resolvedContent = content || (isUnarchive
3592
- ? intl.formatMessage(count > 1 ? messages.unarchiveTemplateContent : messages.unarchiveTemplateSingleContent)
3593
- : intl.formatMessage(count > 1 ? messages.archiveTemplateContent : messages.archiveTemplateSingleContent));
3594
- // AntD v3 footer order is [cancelButton][okButton]. Swap text+handler so
3595
- // "Confirm" (primary) appears on the left and "Cancel" (default) on the right.
3596
- CapModal.confirm({
3597
- title,
3598
- content: resolvedContent,
3599
- icon: ' ',
3600
- className: 'archive-confirm-modal',
3601
- maskStyle: { backgroundColor: 'rgba(0, 0, 0, 0.25)' },
3602
- cancelText: confirmText,
3603
- okText: cancelText,
3604
- cancelButtonProps: { type: 'primary' },
3605
- okButtonProps: { type: 'default' },
3606
- onCancel: (close) => { onConfirm(); close(); },
3607
- // onOk (the right "Cancel" button) just closes the modal — default behaviour
3608
- });
3609
- }
3610
-
3611
3462
  populateTemplatesList = (data, blankTemplateRequired, layoutSelection) => {
3612
3463
  if (!data) {
3613
3464
  return [];
@@ -3757,78 +3608,20 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
3757
3608
  deleteOption = this.props.intl.formatMessage(messages.unMapButton);
3758
3609
  }
3759
3610
  if (!layoutSelection) {
3760
- // Determine archive eligibility for this template:
3761
- // - Zalo: never eligible
3762
- // - WhatsApp: not eligible when status is awaitingApproval / pending / unsubmitted
3763
- // - RCS: not eligible when status is awaitingApproval / pending
3764
- const templateWhatsappStatus = get(template, `versions.base.content.${WHATSAPP_LOWERCASE}.status`, '');
3765
- const templateRcsStatus = get(template, 'versions.base.content.RCS.rcsContent.cardContent[0].Status', '');
3766
- const channelUpCase = this.state.channel.toUpperCase();
3767
- const isTemplateArchiveEligible = channelUpCase !== ZALO &&
3768
- !(channelUpCase === WHATSAPP && [WHATSAPP_STATUSES.awaitingApproval, WHATSAPP_STATUSES.pending, WHATSAPP_STATUSES.unsubmitted].includes(templateWhatsappStatus)) &&
3769
- !(channelUpCase === RCS && [RCS_STATUSES.awaitingApproval, RCS_STATUSES.pending].includes(templateRcsStatus));
3770
-
3771
- // Checkbox on card header (full mode only, only for archive-eligible templates)
3772
- if (this.props.isFullMode && this.props.location.query.type !== 'embedded' && isTemplateArchiveEligible) {
3773
- const selectedIds = get(this.props, 'Templates.selectedTemplateIds', []);
3774
- const selectedIdsArray = selectedIds.toJS ? selectedIds.toJS() : selectedIds;
3775
- temp.cardTop = (
3776
- <div className="template-card-top-bar">
3777
- <CapCheckbox
3778
- checked={selectedIdsArray.includes(template._id)}
3779
- onChange={() => this.props.actions.toggleTemplateSelection(template._id)}
3780
- onClick={(e) => e.stopPropagation()}
3781
- />
3782
- </div>
3783
- );
3784
- }
3785
-
3786
3611
  temp.footer = (
3787
3612
  <div className="footer-container">
3788
3613
  <div className="card-title">
3789
3614
  <span className="template-name" style={{ fontWeight: `${this.state.channel.toLowerCase() === 'wechat' ? '400' : '600'}` }}>
3790
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>}
3791
3616
  {template?.name}
3792
- {template.isArchived && <CapColoredTag tagColor={CAP_G08} tagTextColor={CAP_G05} className="archived-tag">{this.props.intl.formatMessage(messages.archivedTag)}</CapColoredTag>}
3793
3617
  </span>
3794
3618
  {this.props.location.query.type !== 'embedded' && <CapPopover
3795
3619
  trigger="click"
3796
3620
  content={
3797
3621
  <div className="popover-content">
3798
- {this.state.channel !== 'wechat' && !template.isArchived && <div className="popover-action-container">
3622
+ {this.state.channel !== 'wechat' && <div className="popover-action-container">
3799
3623
  <span onClick={() => this.duplicateTemplate(template)} className="popover-action" style={{cursor: 'pointer', padding: '8px 0px'}}>{this.props.intl.formatMessage(messages.duplicateButton)}</span>
3800
3624
  </div>}
3801
- {this.props.isFullMode && isTemplateArchiveEligible && !template.isArchived && <div className="popover-action-container">
3802
- <span
3803
- onClick={() => {
3804
- this.showArchiveConfirm({
3805
- title: this.props.intl.formatMessage(messages.archiveTemplates),
3806
- onConfirm: () => this.props.actions.archiveTemplate(this.state.channel, template._id, template.name),
3807
- count: 1,
3808
- });
3809
- }}
3810
- className="popover-action popover-archive-action"
3811
- >
3812
- <CapIcon type="archive" size="l" />
3813
- {this.props.intl.formatMessage(messages.archiveButton)}
3814
- </span>
3815
- </div>}
3816
- {this.props.isFullMode && isTemplateArchiveEligible && template.isArchived && <div className="popover-action-container">
3817
- <span
3818
- onClick={() => {
3819
- this.showArchiveConfirm({
3820
- title: this.props.intl.formatMessage(messages.unarchiveTemplates),
3821
- onConfirm: () => this.props.actions.unarchiveTemplate(this.state.channel, template._id, template.name),
3822
- count: 1,
3823
- isUnarchive: true,
3824
- });
3825
- }}
3826
- className="popover-action popover-archive-action"
3827
- >
3828
- <CapIcon type="archive" size="l" />
3829
- {this.props.intl.formatMessage(messages.unarchiveButton)}
3830
- </span>
3831
- </div>}
3832
3625
  <div className="popover-action-container">
3833
3626
  <span onClick={() => this.toggleDeleteTemplateModal(template)} className="popover-action" style={{cursor: 'pointer', padding: '8px 0px'}}>{deleteOption}</span>
3834
3627
  </div>
@@ -4421,16 +4214,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4421
4214
  if (([WHATSAPP_LOWERCASE, ZALO_LOWERCASE, RCS_LOWERCASE].includes(this.state?.channel?.toLocaleLowerCase()) && isEmpty(this.state?.hostName))) {
4422
4215
  isfilterContentVisisble = false;
4423
4216
  }
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
4217
  const filterContent = (( isfilterContentVisisble || [WECHAT, MOBILE_PUSH, INAPP].includes(this.state.channel.toUpperCase())) && <div className="action-container">
4430
4218
  {isfilterContentVisisble && <CapInput.Search
4431
4219
  className="search-text"
4432
4220
  style={{width: '210px'}}
4433
- placeholder={_isArchivedMode ? this.props.intl.formatMessage(messages.searchArchivedTemplates) : this.props.intl.formatMessage(messages.searchText)}
4221
+ placeholder={this.props.intl.formatMessage(messages.searchText)}
4434
4222
  value={this.state.searchText}
4435
4223
  onChange={(e) => this.searchTemplate(e.target.value, this.state.channel)}
4436
4224
  disabled={this.checkSearchDisabled()}
@@ -4582,9 +4370,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4582
4370
  </div>
4583
4371
  )
4584
4372
  }
4585
- <div className="template-listing-header-actions">
4586
- {!_isArchivedMode && !_renderHasSelection && (
4587
- 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)? (
4588
4376
  <CapTooltip title={whatsappCountExceedText}>
4589
4377
  <div className="button-disabled-tooltip-wrapper">
4590
4378
  {createButton}
@@ -4592,35 +4380,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4592
4380
  </CapTooltip>
4593
4381
  )
4594
4382
  : 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>
4615
- }
4616
- placement="bottomRight"
4617
- >
4618
- <CapButton type="flat" className="template-listing-more-btn">
4619
- <CapIcon type="more" />
4620
- </CapButton>
4621
- </CapDropdown>
4622
- )}
4383
+ }
4623
4384
  </div>
4385
+
4624
4386
  </div>);
4625
4387
  let htmlPreviewContent = "";
4626
4388
  if (this.state.channel.toLowerCase() === 'ebill') {
@@ -4669,24 +4431,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
4669
4431
  }
4670
4432
  />
4671
4433
 
4672
- {/* Archived mode header with back arrow (full mode only) */}
4673
- {this.props.isFullMode && get(this.props, 'Templates.isArchivedMode', false) && (
4674
- <CapRow type="flex" align="middle" className="archived-mode-header">
4675
- <CapIcon
4676
- type="back"
4677
- className="archived-mode-back-icon"
4678
- onClick={() => {
4679
- this.props.actions.setArchivedMode(false);
4680
- this.setState({ searchText: '', page: 1 }, () => {
4681
- const params = { name: '', sortBy: this.state.sortBy, archiveStatus: 'active' };
4682
- this.getAllTemplates({ params, resetPage: true }, true);
4683
- });
4684
- }}
4685
- />
4686
- <CapHeading type="h3"><FormattedMessage {...messages.archivedTemplates} /></CapHeading>
4687
- </CapRow>
4688
- )}
4689
-
4690
4434
  {channel.toLowerCase() === WHATSAPP_LOWERCASE &&
4691
4435
  showWhatsappCountWarning ? (
4692
4436
  <CapAlert message={whatsappCountExceedText} type="info" />
@@ -618,72 +618,4 @@ export default defineMessages({
618
618
  id: `${scope}.templateUpdateSuccess`,
619
619
  defaultMessage: 'Template updated successfully',
620
620
  },
621
- "archiveTemplates": {
622
- id: `${scope}.archiveTemplates`,
623
- defaultMessage: 'Archive templates',
624
- },
625
- "archiveTemplateContent": {
626
- id: `${scope}.archiveTemplateContent`,
627
- defaultMessage: 'These templates will be archived and unavailable for use. You can restore them anytime.',
628
- },
629
- "archiveTemplateSingleContent": {
630
- id: `${scope}.archiveTemplateSingleContent`,
631
- defaultMessage: 'This template will be archived and unavailable for use. You can restore it anytime.',
632
- },
633
- "unarchiveTemplates": {
634
- id: `${scope}.unarchiveTemplates`,
635
- defaultMessage: 'Unarchive templates',
636
- },
637
- "unarchiveTemplateContent": {
638
- id: `${scope}.unarchiveTemplateContent`,
639
- defaultMessage: 'These templates will be unarchived and available for use again.',
640
- },
641
- "unarchiveTemplateSingleContent": {
642
- id: `${scope}.unarchiveTemplateSingleContent`,
643
- defaultMessage: 'This template will be unarchived and available for use again.',
644
- },
645
- "archiveConfirmOk": {
646
- id: `${scope}.archiveConfirmOk`,
647
- defaultMessage: 'Confirm',
648
- },
649
- "archiveConfirmCancel": {
650
- id: `${scope}.archiveConfirmCancel`,
651
- defaultMessage: 'Cancel',
652
- },
653
- "archiveButton": {
654
- id: `${scope}.archiveButton`,
655
- defaultMessage: 'Archive',
656
- },
657
- "unarchiveButton": {
658
- id: `${scope}.unarchiveButton`,
659
- defaultMessage: 'Unarchive',
660
- },
661
- "archivedTag": {
662
- id: `${scope}.archivedTag`,
663
- defaultMessage: 'Archived',
664
- },
665
- "archivedTemplates": {
666
- id: `${scope}.archivedTemplates`,
667
- defaultMessage: 'Archived templates',
668
- },
669
- "searchArchivedTemplates": {
670
- id: `${scope}.searchArchivedTemplates`,
671
- defaultMessage: 'Search archived templates...',
672
- },
673
- "templatesSelected": {
674
- id: `${scope}.templatesSelected`,
675
- defaultMessage: '{count} {count, plural, one {template} other {templates}} selected',
676
- },
677
- "cannotEditArchivedTemplate": {
678
- id: `${scope}.cannotEditArchivedTemplate`,
679
- defaultMessage: 'Cannot edit an archived template. Please unarchive it first.',
680
- },
681
- "noArchivedCreatives": {
682
- id: `${scope}.noArchivedCreatives`,
683
- defaultMessage: 'No archived creatives',
684
- },
685
- "noArchivedCreativesDesc": {
686
- id: `${scope}.noArchivedCreativesDesc`,
687
- defaultMessage: 'Creatives you archive will appear here',
688
- },
689
621
  });
@@ -26,13 +26,6 @@ export const initialState = fromJS({
26
26
  reportsSettings: {},
27
27
  },
28
28
  senderDetails: {},
29
- archiveFilter: 'active',
30
- isArchivedMode: false,
31
- selectedTemplateIds: fromJS([]),
32
- archiveInProgress: false,
33
- unarchiveInProgress: false,
34
- bulkArchiveInProgress: false,
35
- bulkUnarchiveInProgress: false,
36
29
  });
37
30
 
38
31
  function templatesReducer(state = initialState, action) {
@@ -238,67 +231,6 @@ function templatesReducer(state = initialState, action) {
238
231
  hostName: '',
239
232
  errors: action.payload,
240
233
  });
241
- case types.SET_ARCHIVE_FILTER:
242
- return state
243
- .set('archiveFilter', action.filter)
244
- .set('templates', [])
245
- .set('selectedTemplateIds', fromJS([]));
246
- case types.SET_ARCHIVED_MODE:
247
- return state
248
- .set('isArchivedMode', action.isArchived)
249
- .set('archiveFilter', action.isArchived ? 'archived' : 'active')
250
- .set('templates', [])
251
- .set('selectedTemplateIds', fromJS([]));
252
- case types.TOGGLE_TEMPLATE_SELECTION: {
253
- const rawSelected = state.get('selectedTemplateIds');
254
- // Defensive: handle undefined (stale persisted state) or plain array (pre-fromJS migration)
255
- const currentSelected = rawSelected && typeof rawSelected.toJS === 'function'
256
- ? rawSelected.toJS()
257
- : (Array.isArray(rawSelected) ? rawSelected : []);
258
- const idx = currentSelected.indexOf(action.id);
259
- const newSelected = idx === -1
260
- ? [...currentSelected, action.id]
261
- : currentSelected.filter((id) => id !== action.id);
262
- return state.set('selectedTemplateIds', fromJS(newSelected));
263
- }
264
- case types.SELECT_ALL_TEMPLATES:
265
- return state.set('selectedTemplateIds', fromJS(action.ids));
266
- case types.CLEAR_TEMPLATE_SELECTION:
267
- return state.set('selectedTemplateIds', fromJS([]));
268
- case types.ARCHIVE_TEMPLATE_REQUEST:
269
- return state.set('archiveInProgress', true);
270
- case types.ARCHIVE_TEMPLATE_SUCCESS: {
271
- const afterArchive = state.get('selectedTemplateIds');
272
- const archiveSelected = afterArchive && typeof afterArchive.toJS === 'function' ? afterArchive.toJS() : [];
273
- return state
274
- .set('archiveInProgress', false)
275
- .set('selectedTemplateIds', fromJS(archiveSelected.filter((sid) => sid !== action.id)));
276
- }
277
- case types.ARCHIVE_TEMPLATE_FAILURE:
278
- return state.set('archiveInProgress', false);
279
- case types.UNARCHIVE_TEMPLATE_REQUEST:
280
- return state.set('unarchiveInProgress', true);
281
- case types.UNARCHIVE_TEMPLATE_SUCCESS: {
282
- const afterUnarchive = state.get('selectedTemplateIds');
283
- const unarchiveSelected = afterUnarchive && typeof afterUnarchive.toJS === 'function' ? afterUnarchive.toJS() : [];
284
- return state
285
- .set('unarchiveInProgress', false)
286
- .set('selectedTemplateIds', fromJS(unarchiveSelected.filter((sid) => sid !== action.id)));
287
- }
288
- case types.UNARCHIVE_TEMPLATE_FAILURE:
289
- return state.set('unarchiveInProgress', false);
290
- case types.BULK_ARCHIVE_REQUEST:
291
- return state.set('bulkArchiveInProgress', true);
292
- case types.BULK_ARCHIVE_SUCCESS:
293
- return state.set('bulkArchiveInProgress', false).set('selectedTemplateIds', fromJS([]));
294
- case types.BULK_ARCHIVE_FAILURE:
295
- return state.set('bulkArchiveInProgress', false);
296
- case types.BULK_UNARCHIVE_REQUEST:
297
- return state.set('bulkUnarchiveInProgress', true);
298
- case types.BULK_UNARCHIVE_SUCCESS:
299
- return state.set('bulkUnarchiveInProgress', false).set('selectedTemplateIds', fromJS([]));
300
- case types.BULK_UNARCHIVE_FAILURE:
301
- return state.set('bulkUnarchiveInProgress', false);
302
234
  default:
303
235
  return state;
304
236
  }