@capillarytech/creatives-library 8.0.340-beta.0.5 → 8.0.340-beta.0.6
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.
- package/constants/unified.js +1 -0
- package/package.json +1 -1
- package/services/api.js +20 -0
- package/services/tests/api.test.js +59 -0
- package/utils/common.js +6 -0
- package/utils/test-utils.js +2 -2
- package/utils/tests/v2Common.test.js +46 -1
- package/utils/v2common.js +18 -0
- package/v2Components/CapTagList/index.js +5 -6
- package/v2Components/CommonTestAndPreview/UnifiedPreview/WhatsAppPreviewContent.js +18 -6
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +27 -0
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/WhatsAppPreviewContent.test.js +48 -0
- package/v2Components/TemplatePreview/_templatePreview.scss +21 -0
- package/v2Components/TemplatePreview/index.js +18 -6
- package/v2Components/TemplatePreview/tests/__snapshots__/index.test.js.snap +1 -0
- package/v2Containers/Assets/images/archive_Empty_Illustration.svg +9 -0
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +28 -20
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +24 -16
- package/v2Containers/CreativesContainer/SlideBoxContent.js +16 -5
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +3 -1
- package/v2Containers/CreativesContainer/index.js +14 -1
- package/v2Containers/CreativesContainer/messages.js +4 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +2 -4
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +3 -0
- package/v2Containers/Email/reducer.js +12 -3
- package/v2Containers/Email/sagas.js +9 -4
- package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +4 -0
- package/v2Containers/Email/tests/reducer.test.js +47 -0
- package/v2Containers/Email/tests/sagas.test.js +146 -6
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +8 -1
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +1 -0
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +7 -0
- package/v2Containers/Sms/Create/index.js +3 -0
- package/v2Containers/Templates/ChannelTypeIllustration.js +23 -6
- package/v2Containers/Templates/_templates.scss +155 -24
- package/v2Containers/Templates/actions.js +44 -0
- package/v2Containers/Templates/constants.js +31 -0
- package/v2Containers/Templates/index.js +400 -59
- package/v2Containers/Templates/messages.js +96 -0
- package/v2Containers/Templates/reducer.js +84 -1
- package/v2Containers/Templates/sagas.js +64 -0
- package/v2Containers/Templates/selectors.js +12 -0
- package/v2Containers/Templates/tests/ChannelTypeIllustration.test.js +12 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1166 -1112
- package/v2Containers/Templates/tests/index.test.js +6 -0
- package/v2Containers/Templates/tests/reducer.test.js +178 -0
- package/v2Containers/Templates/tests/sagas.test.js +390 -8
- package/v2Containers/Templates/tests/selector.test.js +32 -0
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -1
- package/v2Containers/Viber/constants.js +8 -0
- package/v2Containers/Viber/index.js +5 -0
- package/v2Containers/Viber/messages.js +4 -0
- package/v2Containers/Viber/reducer.js +26 -3
- package/v2Containers/Viber/sagas.js +50 -8
- package/v2Containers/Viber/tests/index.test.js +80 -0
- package/v2Containers/Viber/tests/reducer.test.js +297 -0
- package/v2Containers/Viber/tests/saga.test.js +412 -40
- package/v2Containers/Whatsapp/constants.js +8 -0
- package/v2Containers/Whatsapp/index.js +145 -5
- package/v2Containers/Whatsapp/index.scss +12 -0
- package/v2Containers/Whatsapp/messages.js +16 -0
|
@@ -50,9 +50,8 @@ import {
|
|
|
50
50
|
CapStatus,
|
|
51
51
|
CapColoredTag,
|
|
52
52
|
CapSpin,
|
|
53
|
-
|
|
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
|
|
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
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
INAPP_LAYOUT_DETAILS[inappBodyType]?.
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
{
|
|
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 !==
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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 !==
|
|
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
|
|
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 && (
|