@capillarytech/creatives-library 9.0.70 → 9.0.72
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 +0 -1
- package/package.json +1 -1
- package/utils/common.js +0 -6
- package/v2Containers/Cap/tests/index.test.js +1 -0
- package/v2Containers/CreativesContainer/index.js +2 -2
- package/v2Containers/Templates/_templates.scss +0 -7
- package/v2Containers/Templates/constants.js +1 -0
- package/v2Containers/Templates/index.js +56 -52
- package/v2Containers/Templates/messages.js +4 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +232 -45
- package/v2Containers/Templates/tests/archival.test.js +242 -0
package/constants/unified.js
CHANGED
|
@@ -59,7 +59,6 @@ export const EXTENDED_TAG = 'ExtendedTagMessage';
|
|
|
59
59
|
export const BADGES_UI_ENABLED = 'BADGES_UI_ENABLED';
|
|
60
60
|
export const JP_LOCALE_HIDE_FEATURE = 'JP_LOCALE_HIDE_FEATURE';
|
|
61
61
|
export const ENABLE_WECHAT = 'ENABLE_WECHAT';
|
|
62
|
-
export const ENABLE_CREATIVES_ARCHIVAL = 'ENABLE_CREATIVES_ARCHIVAL';
|
|
63
62
|
export const ENABLE_WEBPUSH = 'ENABLE_WEBPUSH';
|
|
64
63
|
export const ENABLE_CUSTOMER_BARCODE_TAG = 'ENABLE_CUSTOMER_BARCODE_TAG';
|
|
65
64
|
export const EMAIL_UNSUBSCRIBE_TAG_MANDATORY = 'EMAIL_UNSUBSCRIBE_TAG_MANDATORY';
|
package/package.json
CHANGED
package/utils/common.js
CHANGED
|
@@ -29,7 +29,6 @@ import {
|
|
|
29
29
|
ENABLE_NEW_FORMBUILDER_SMS,
|
|
30
30
|
ENABLE_NEW_FORMBUILDER_MPUSH,
|
|
31
31
|
SUPPORT_ENGAGEMENT_MODULE,
|
|
32
|
-
ENABLE_CREATIVES_ARCHIVAL,
|
|
33
32
|
} from '../constants/unified';
|
|
34
33
|
import { apiMessageFormatHandler } from './commonUtils';
|
|
35
34
|
|
|
@@ -121,11 +120,6 @@ export const hasWechatFeatureEnabled = Auth.hasFeatureAccess.bind(
|
|
|
121
120
|
ENABLE_WECHAT,
|
|
122
121
|
);
|
|
123
122
|
|
|
124
|
-
export const hasCreativesArchivalEnabled = Auth.hasFeatureAccess.bind(
|
|
125
|
-
null,
|
|
126
|
-
ENABLE_CREATIVES_ARCHIVAL,
|
|
127
|
-
);
|
|
128
|
-
|
|
129
123
|
export const hasWebPushFeatureEnabled = Auth.hasFeatureAccess.bind(
|
|
130
124
|
null,
|
|
131
125
|
ENABLE_WEBPUSH,
|
|
@@ -8,6 +8,7 @@ import { shallowWithIntl } from '../../../helpers/intl-enzym-test-helpers';
|
|
|
8
8
|
const { match, location, capGlobal, language } = mockData;
|
|
9
9
|
|
|
10
10
|
jest.mock('@capillarytech/cap-ui-utils', () => ({
|
|
11
|
+
getEnvVariable: jest.fn(),
|
|
11
12
|
GA: {
|
|
12
13
|
initialize: jest.fn(),
|
|
13
14
|
timeTracker: {
|
|
@@ -320,7 +320,7 @@ export class Creatives extends React.Component {
|
|
|
320
320
|
};
|
|
321
321
|
|
|
322
322
|
onEditTemplate = () => {
|
|
323
|
-
if (
|
|
323
|
+
if (this.props.templateData?.isArchived) {
|
|
324
324
|
CapNotification.error({ message: this.props.intl.formatMessage(messages.cannotEditArchivedTemplate) });
|
|
325
325
|
return;
|
|
326
326
|
}
|
|
@@ -2472,7 +2472,7 @@ export class Creatives extends React.Component {
|
|
|
2472
2472
|
onSave={this.saveMessage}
|
|
2473
2473
|
onDiscard={this.discardMessage}
|
|
2474
2474
|
onEditTemplate={this.onEditTemplate}
|
|
2475
|
-
isTemplateArchived={!!(
|
|
2475
|
+
isTemplateArchived={!!(this.props.templateData && this.props.templateData.isArchived)}
|
|
2476
2476
|
slidBoxContent={slidBoxContent}
|
|
2477
2477
|
onCreateNextStep={this.onCreateNextStep}
|
|
2478
2478
|
currentChannel={currentChannel.toUpperCase()}
|
|
@@ -1601,13 +1601,6 @@
|
|
|
1601
1601
|
padding: $CAP_SPACE_08 $CAP_SPACE_12 0;
|
|
1602
1602
|
}
|
|
1603
1603
|
|
|
1604
|
-
.template-listing-header-actions {
|
|
1605
|
-
display: flex;
|
|
1606
|
-
justify-content: space-between;
|
|
1607
|
-
align-items: center;
|
|
1608
|
-
gap: $CAP_SPACE_08;
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1611
1604
|
.template-listing-more-btn {
|
|
1612
1605
|
padding-inline: $CAP_SPACE_20;
|
|
1613
1606
|
min-width: auto;
|
|
@@ -95,6 +95,7 @@ export const noApprovedRcsTemplatesTitle='noApprovedRcsTemplatesTitle'
|
|
|
95
95
|
export const noApprovedRcsTemplatesDesc='noApprovedRcsTemplatesDesc'
|
|
96
96
|
|
|
97
97
|
// Archive feature
|
|
98
|
+
export const DEFAULT_ARCHIVAL_LIMIT = 25;
|
|
98
99
|
export const ARCHIVE_TEMPLATE_REQUEST = 'app/v2Containers/Templates/ARCHIVE_TEMPLATE_REQUEST';
|
|
99
100
|
export const ARCHIVE_TEMPLATE_SUCCESS = 'app/v2Containers/Templates/ARCHIVE_TEMPLATE_SUCCESS';
|
|
100
101
|
export const ARCHIVE_TEMPLATE_FAILURE = 'app/v2Containers/Templates/ARCHIVE_TEMPLATE_FAILURE';
|
|
@@ -117,7 +117,7 @@ import {
|
|
|
117
117
|
URL,
|
|
118
118
|
SITE_URL,
|
|
119
119
|
} from '../App/constants';
|
|
120
|
-
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';
|
|
120
|
+
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, DEFAULT_ARCHIVAL_LIMIT} from './constants';
|
|
121
121
|
import { COPY_OF, EMBEDDED } from '../../constants/unified';
|
|
122
122
|
import { sanitizeFileName, exportEmailTemplateHtml } from '../../utils/downloadFile';
|
|
123
123
|
import {
|
|
@@ -152,7 +152,7 @@ import {
|
|
|
152
152
|
CAP_SPACE_16, CAP_G08, CAP_G05, CAP_SPACE_08, CAP_SPACE_12, CAP_YELLOW01,
|
|
153
153
|
} from '@capillarytech/cap-ui-library/styled/variables';
|
|
154
154
|
import { SMS_CATEGORY_TAG_STYLES, SMS_CATEGORY_LABELS } from '../../v2Components/SmsFallback/constants';
|
|
155
|
-
import { GA } from '@capillarytech/cap-ui-utils';
|
|
155
|
+
import { GA, getEnvVariable } from '@capillarytech/cap-ui-utils';
|
|
156
156
|
import { MAPP_SDK } from '../InApp/constants';
|
|
157
157
|
import injectReducer from '../../utils/injectReducer';
|
|
158
158
|
import v2TemplateReducer from './reducer';
|
|
@@ -186,6 +186,7 @@ const AccountScrollDiv = styled.div`
|
|
|
186
186
|
overflow: scroll;
|
|
187
187
|
height: calc(100vh - 18.75rem);
|
|
188
188
|
`;
|
|
189
|
+
const ARCHIVAL_LIMIT = parseInt(getEnvVariable('ARCHIVAL_LIMIT'), 10) || DEFAULT_ARCHIVAL_LIMIT;
|
|
189
190
|
const ACCOUNT_SELECTION_MODE = "accountSelectionMode";
|
|
190
191
|
const ACCOUNT_CHANGE_MODE = 'accountChangeMode';
|
|
191
192
|
const TEMPLATES_MODE = "templatesMode";
|
|
@@ -2159,10 +2160,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2159
2160
|
// Archive eligibility per template: Zalo never; WhatsApp/RCS not when pending/awaiting
|
|
2160
2161
|
const cardWhatsappStatus = get(template, `versions.base.content.${WHATSAPP_LOWERCASE}.status`, '');
|
|
2161
2162
|
const cardRcsStatus = get(template, 'versions.base.content.RCS.rcsContent.cardContent[0].Status', '');
|
|
2162
|
-
const
|
|
2163
|
-
const
|
|
2164
|
-
const
|
|
2165
|
-
const isAnyArchiveInProgress = isArchivalEnabled && !!(get(this.props, 'Templates.archiveInProgress') || get(this.props, 'Templates.unarchiveInProgress') || get(this.props, 'Templates.bulkArchiveInProgress') || get(this.props, 'Templates.bulkUnarchiveInProgress'));
|
|
2163
|
+
const isCardArchiveEligible = this.isChannelArchiveEligible(currentChannel, cardWhatsappStatus, cardRcsStatus);
|
|
2164
|
+
const isArchivedMode = get(this.props, 'Templates.isArchivedMode', false);
|
|
2165
|
+
const isAnyArchiveInProgress = !!(get(this.props, 'Templates.archiveInProgress') || get(this.props, 'Templates.unarchiveInProgress') || get(this.props, 'Templates.bulkArchiveInProgress') || get(this.props, 'Templates.bulkUnarchiveInProgress'));
|
|
2166
2166
|
const templateData = {
|
|
2167
2167
|
key: `${currentChannel}-card-${template?.name}`,
|
|
2168
2168
|
title: (
|
|
@@ -2307,7 +2307,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2307
2307
|
</CapMenu.Item>
|
|
2308
2308
|
)}
|
|
2309
2309
|
{/* Archive/Unarchive menu item (full mode only, not for Zalo, not for WhatsApp/RCS awaiting/pending) */}
|
|
2310
|
-
{
|
|
2310
|
+
{(() => {
|
|
2311
2311
|
const channelUp = this.state.channel.toUpperCase();
|
|
2312
2312
|
if (!this.isChannelArchiveEligible(channelUp, status, rcsStatus)) return null;
|
|
2313
2313
|
const canArchive = this.canPerform(PERMISSIONS.CREATIVE_DELETE);
|
|
@@ -2928,12 +2928,12 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2928
2928
|
|
|
2929
2929
|
const noLoaderAndSearchText = isEmpty(this.state.searchText) && !isLoading;
|
|
2930
2930
|
|
|
2931
|
-
const
|
|
2932
|
-
const isArchivedModeLocal = isArchivalEnabledLocal && get(this.props, 'Templates.isArchivedMode', false);
|
|
2931
|
+
const isArchivedModeLocal = get(this.props, 'Templates.isArchivedMode', false);
|
|
2933
2932
|
const selectedIdsLocal = get(this.props, 'Templates.selectedTemplateIds', []);
|
|
2934
2933
|
const selectedIdsArrayLocal = selectedIdsLocal && typeof selectedIdsLocal.toJS === 'function' ? selectedIdsLocal.toJS() : (Array.isArray(selectedIdsLocal) ? selectedIdsLocal : []);
|
|
2935
2934
|
const selectedCountLocal = selectedIdsArrayLocal.length;
|
|
2936
|
-
const hasSelectionLocal =
|
|
2935
|
+
const hasSelectionLocal = this.props.isFullMode && selectedCountLocal > 0;
|
|
2936
|
+
const isArchiveLimitExceeded = selectedCountLocal > ARCHIVAL_LIMIT;
|
|
2937
2937
|
|
|
2938
2938
|
return (<div>
|
|
2939
2939
|
{[WECHAT, MOBILE_PUSH, WEBPUSH, INAPP, WHATSAPP, ZALO, RCS].includes(currentChannel) && this.showAccountName()}
|
|
@@ -2945,16 +2945,21 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2945
2945
|
{this.props.intl.formatMessage(messages.templatesSelected, { count: selectedCountLocal })}
|
|
2946
2946
|
</CapLabel>
|
|
2947
2947
|
<NoPermissionWrapper allowed={this.canPerform(PERMISSIONS.CREATIVE_DELETE)}>
|
|
2948
|
-
<
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2948
|
+
<CapTooltip title={isArchiveLimitExceeded ? this.props.intl.formatMessage(messages.archiveLimitExceeded, { limit: ARCHIVAL_LIMIT }) : ''}>
|
|
2949
|
+
<span>
|
|
2950
|
+
<CapButton
|
|
2951
|
+
type="primary"
|
|
2952
|
+
className="archive-template-button"
|
|
2953
|
+
disabled={isArchiveLimitExceeded}
|
|
2954
|
+
prefix={<CapIcon type="archive" size="s" />}
|
|
2955
|
+
onClick={() => this.handleBulkArchiveAction({ ids: selectedIdsArrayLocal, count: selectedCountLocal, isUnarchive: isArchivedModeLocal })}
|
|
2956
|
+
>
|
|
2957
|
+
<span className="archive-btn-label">
|
|
2958
|
+
<FormattedMessage {...(isArchivedModeLocal ? messages.unarchiveButton : messages.archiveButton)} />
|
|
2959
|
+
</span>
|
|
2960
|
+
</CapButton>
|
|
2956
2961
|
</span>
|
|
2957
|
-
</
|
|
2962
|
+
</CapTooltip>
|
|
2958
2963
|
</NoPermissionWrapper>
|
|
2959
2964
|
<CapButton type="secondary" onClick={() => this.props.actions.clearTemplateSelection()}>
|
|
2960
2965
|
<FormattedMessage {...messages.archiveConfirmCancel} />
|
|
@@ -4195,7 +4200,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4195
4200
|
<span className="template-name" style={{ fontWeight: `${this.state.channel.toLowerCase() === 'wechat' ? '400' : '600'}` }}>
|
|
4196
4201
|
{ 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>}
|
|
4197
4202
|
{template?.name}
|
|
4198
|
-
{
|
|
4203
|
+
{template.isArchived && <CapColoredTag tagColor={CAP_G08} tagTextColor={CAP_G05} className="archived-tag">{this.props.intl.formatMessage(messages.archivedTag)}</CapColoredTag>}
|
|
4199
4204
|
</span>
|
|
4200
4205
|
{this.props.location.query.type !== EMBEDDED && <CapPopover
|
|
4201
4206
|
trigger="click"
|
|
@@ -4208,7 +4213,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4208
4213
|
</CapButton>
|
|
4209
4214
|
</NoPermissionWrapper>
|
|
4210
4215
|
</div>}
|
|
4211
|
-
{
|
|
4216
|
+
{this.props.isFullMode && isTemplateArchiveEligible && !template.isArchived && <div className="popover-action-container">
|
|
4212
4217
|
<NoPermissionWrapper allowed={this.canPerform(PERMISSIONS.CREATIVE_DELETE)}>
|
|
4213
4218
|
<CapButton
|
|
4214
4219
|
type="link"
|
|
@@ -4220,7 +4225,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4220
4225
|
</CapButton>
|
|
4221
4226
|
</NoPermissionWrapper>
|
|
4222
4227
|
</div>}
|
|
4223
|
-
{
|
|
4228
|
+
{this.props.isFullMode && isTemplateArchiveEligible && template.isArchived && <div className="popover-action-container">
|
|
4224
4229
|
<NoPermissionWrapper allowed={this.canPerform(PERMISSIONS.CREATIVE_DELETE)}>
|
|
4225
4230
|
<CapButton
|
|
4226
4231
|
type="link"
|
|
@@ -4867,11 +4872,10 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4867
4872
|
if (([WHATSAPP_LOWERCASE, ZALO_LOWERCASE, RCS_LOWERCASE].includes(this.state?.channel?.toLocaleLowerCase()) && isEmpty(this.state?.hostName))) {
|
|
4868
4873
|
isfilterContentVisisble = false;
|
|
4869
4874
|
}
|
|
4870
|
-
const
|
|
4871
|
-
const _isArchivedMode = _isArchivalEnabled && get(this.props, 'Templates.isArchivedMode', false);
|
|
4875
|
+
const _isArchivedMode = get(this.props, 'Templates.isArchivedMode', false);
|
|
4872
4876
|
const _renderSelectedIds = get(this.props, 'Templates.selectedTemplateIds', []);
|
|
4873
4877
|
const _renderSelectedIdsArray = _renderSelectedIds && typeof _renderSelectedIds.toJS === 'function' ? _renderSelectedIds.toJS() : (Array.isArray(_renderSelectedIds) ? _renderSelectedIds : []);
|
|
4874
|
-
const _renderHasSelection =
|
|
4878
|
+
const _renderHasSelection = this.props.isFullMode && _renderSelectedIdsArray.length > 0;
|
|
4875
4879
|
|
|
4876
4880
|
const useLocalTemplates = this.props.localTemplatesConfig?.useLocalTemplates;
|
|
4877
4881
|
const builtFilterContent = ((isfilterContentVisisble || [WECHAT, MOBILE_PUSH, INAPP].includes(this.state.channel.toUpperCase())) && (
|
|
@@ -5031,34 +5035,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
5031
5035
|
</div>
|
|
5032
5036
|
)
|
|
5033
5037
|
}
|
|
5034
|
-
<div className="template-listing-header-actions">
|
|
5035
|
-
{/* More (⋯) menu: full mode only, not archived mode, not Zalo (no archive support), not when selection active, archive flag enabled */}
|
|
5036
|
-
{commonUtil.hasCreativesArchivalEnabled() && !_isArchivedMode && !_renderHasSelection && this.props.isFullMode && this.props.location.query.type !== EMBEDDED && channelLowerCase !== ZALO_LOWERCASE && (
|
|
5037
|
-
<CapDropdown
|
|
5038
|
-
trigger={['click']}
|
|
5039
|
-
overlay={
|
|
5040
|
-
<CapMenu>
|
|
5041
|
-
<CapMenu.Item
|
|
5042
|
-
key="archived"
|
|
5043
|
-
onClick={() => this.setArchivedMode(true)}
|
|
5044
|
-
>
|
|
5045
|
-
<FormattedMessage {...messages.archivedTemplates} />
|
|
5046
|
-
</CapMenu.Item>
|
|
5047
|
-
</CapMenu>
|
|
5048
|
-
}
|
|
5049
|
-
placement="bottomRight"
|
|
5050
|
-
>
|
|
5051
|
-
<CapButton type="flat" className="template-listing-more-btn">
|
|
5052
|
-
<CapIcon type="more" />
|
|
5053
|
-
</CapButton>
|
|
5054
|
-
</CapDropdown>
|
|
5055
|
-
)}
|
|
5056
|
-
</div>
|
|
5057
5038
|
</div>
|
|
5058
5039
|
<div>
|
|
5059
|
-
<
|
|
5040
|
+
<CapRow type="flex" align="middle" justify="space-between" gap={8} className="action-container__create-row">
|
|
5060
5041
|
{
|
|
5061
|
-
isfilterContentVisisble && !isWechatEmbedded && !this.props.isDltFromRcs && !this.props.isSmsFallbackFromRcs && (
|
|
5042
|
+
isfilterContentVisisble && !_renderHasSelection && !isWechatEmbedded && !this.props.isDltFromRcs && !this.props.isSmsFallbackFromRcs && (
|
|
5062
5043
|
this.state?.channel?.toLowerCase() === WHATSAPP_LOWERCASE && isWhatsappCountExeeded ? (
|
|
5063
5044
|
<CapTooltip title={whatsappCountExceedText}>
|
|
5064
5045
|
<div className="button-disabled-tooltip-wrapper">
|
|
@@ -5068,7 +5049,30 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
5068
5049
|
) : createButton
|
|
5069
5050
|
)
|
|
5070
5051
|
}
|
|
5071
|
-
|
|
5052
|
+
<CapRow type="flex" align="middle" gap={8} className="template-listing-header-actions">
|
|
5053
|
+
{/* More (⋯) menu: full mode only, not archived mode, not Zalo (no archive support), not when selection active, archive flag enabled */}
|
|
5054
|
+
{!_isArchivedMode && !_renderHasSelection && this.props.isFullMode && this.props.location.query.type !== EMBEDDED && channelLowerCase !== ZALO_LOWERCASE && (
|
|
5055
|
+
<CapDropdown
|
|
5056
|
+
trigger={['click']}
|
|
5057
|
+
overlay={
|
|
5058
|
+
<CapMenu>
|
|
5059
|
+
<CapMenu.Item
|
|
5060
|
+
key="archived"
|
|
5061
|
+
onClick={() => this.setArchivedMode(true)}
|
|
5062
|
+
>
|
|
5063
|
+
<FormattedMessage {...messages.archivedTemplates} />
|
|
5064
|
+
</CapMenu.Item>
|
|
5065
|
+
</CapMenu>
|
|
5066
|
+
}
|
|
5067
|
+
placement="bottomRight"
|
|
5068
|
+
>
|
|
5069
|
+
<CapButton type="flat" className="template-listing-more-btn">
|
|
5070
|
+
<CapIcon type="more" />
|
|
5071
|
+
</CapButton>
|
|
5072
|
+
</CapDropdown>
|
|
5073
|
+
)}
|
|
5074
|
+
</CapRow>
|
|
5075
|
+
</CapRow>
|
|
5072
5076
|
</div>
|
|
5073
5077
|
</div>
|
|
5074
5078
|
));
|
|
@@ -5129,7 +5133,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
5129
5133
|
/>
|
|
5130
5134
|
|
|
5131
5135
|
{/* Archived mode header with back arrow (full mode only, archive flag enabled) */}
|
|
5132
|
-
{
|
|
5136
|
+
{this.props.isFullMode && get(this.props, 'Templates.isArchivedMode', false) && (
|
|
5133
5137
|
<CapRow type="flex" align="middle" className="archived-mode-header">
|
|
5134
5138
|
<CapIcon
|
|
5135
5139
|
type="back"
|
|
@@ -714,6 +714,10 @@ export default defineMessages({
|
|
|
714
714
|
id: `${scope}.unarchiveTemplateSuccess`,
|
|
715
715
|
defaultMessage: 'Template unarchived successfully',
|
|
716
716
|
},
|
|
717
|
+
"archiveLimitExceeded": {
|
|
718
|
+
id: `${scope}.archiveLimitExceeded`,
|
|
719
|
+
defaultMessage: 'Archive limit exceeded, it cannot be greater than {limit}.',
|
|
720
|
+
},
|
|
717
721
|
"bulkArchiveSuccess": {
|
|
718
722
|
id: `${scope}.bulkArchiveSuccess`,
|
|
719
723
|
defaultMessage: '{count} {count, plural, one {template} other {templates}} archived successfully',
|
|
@@ -228,13 +228,14 @@ exports[`Test Templates container Should render sms illustration when no templat
|
|
|
228
228
|
/>
|
|
229
229
|
<Component />
|
|
230
230
|
<Component />
|
|
231
|
-
<div
|
|
232
|
-
className="template-listing-header-actions"
|
|
233
|
-
/>
|
|
234
231
|
</div>
|
|
235
232
|
<div>
|
|
236
|
-
<
|
|
233
|
+
<CapRow
|
|
234
|
+
align="middle"
|
|
237
235
|
className="action-container__create-row"
|
|
236
|
+
gap={8}
|
|
237
|
+
justify="space-between"
|
|
238
|
+
type="flex"
|
|
238
239
|
>
|
|
239
240
|
<NoPermissionWrapper
|
|
240
241
|
allowed={false}
|
|
@@ -248,7 +249,13 @@ exports[`Test Templates container Should render sms illustration when no templat
|
|
|
248
249
|
Create new
|
|
249
250
|
</CapButton>
|
|
250
251
|
</NoPermissionWrapper>
|
|
251
|
-
|
|
252
|
+
<CapRow
|
|
253
|
+
align="middle"
|
|
254
|
+
className="template-listing-header-actions"
|
|
255
|
+
gap={8}
|
|
256
|
+
type="flex"
|
|
257
|
+
/>
|
|
258
|
+
</CapRow>
|
|
252
259
|
</div>
|
|
253
260
|
</div>
|
|
254
261
|
</div>
|
|
@@ -500,13 +507,14 @@ exports[`Test Templates container Should render temlates when whatsapp templates
|
|
|
500
507
|
</CapRow>
|
|
501
508
|
<Component />
|
|
502
509
|
<Component />
|
|
503
|
-
<div
|
|
504
|
-
className="template-listing-header-actions"
|
|
505
|
-
/>
|
|
506
510
|
</div>
|
|
507
511
|
<div>
|
|
508
|
-
<
|
|
512
|
+
<CapRow
|
|
513
|
+
align="middle"
|
|
509
514
|
className="action-container__create-row"
|
|
515
|
+
gap={8}
|
|
516
|
+
justify="space-between"
|
|
517
|
+
type="flex"
|
|
510
518
|
>
|
|
511
519
|
<NoPermissionWrapper
|
|
512
520
|
allowed={false}
|
|
@@ -523,7 +531,13 @@ exports[`Test Templates container Should render temlates when whatsapp templates
|
|
|
523
531
|
title="Create new template"
|
|
524
532
|
/>
|
|
525
533
|
</NoPermissionWrapper>
|
|
526
|
-
|
|
534
|
+
<CapRow
|
|
535
|
+
align="middle"
|
|
536
|
+
className="template-listing-header-actions"
|
|
537
|
+
gap={8}
|
|
538
|
+
type="flex"
|
|
539
|
+
/>
|
|
540
|
+
</CapRow>
|
|
527
541
|
</div>
|
|
528
542
|
</div>
|
|
529
543
|
</div>
|
|
@@ -811,13 +825,14 @@ exports[`Test Templates container Should render temlates when whatsapp templates
|
|
|
811
825
|
</CapRow>
|
|
812
826
|
<Component />
|
|
813
827
|
<Component />
|
|
814
|
-
<div
|
|
815
|
-
className="template-listing-header-actions"
|
|
816
|
-
/>
|
|
817
828
|
</div>
|
|
818
829
|
<div>
|
|
819
|
-
<
|
|
830
|
+
<CapRow
|
|
831
|
+
align="middle"
|
|
820
832
|
className="action-container__create-row"
|
|
833
|
+
gap={8}
|
|
834
|
+
justify="space-between"
|
|
835
|
+
type="flex"
|
|
821
836
|
>
|
|
822
837
|
<NoPermissionWrapper
|
|
823
838
|
allowed={false}
|
|
@@ -831,7 +846,44 @@ exports[`Test Templates container Should render temlates when whatsapp templates
|
|
|
831
846
|
Create new
|
|
832
847
|
</CapButton>
|
|
833
848
|
</NoPermissionWrapper>
|
|
834
|
-
|
|
849
|
+
<CapRow
|
|
850
|
+
align="middle"
|
|
851
|
+
className="template-listing-header-actions"
|
|
852
|
+
gap={8}
|
|
853
|
+
type="flex"
|
|
854
|
+
>
|
|
855
|
+
<CapDropdownComponent
|
|
856
|
+
overlay={
|
|
857
|
+
<CapMenuComponent>
|
|
858
|
+
<withItemHOC
|
|
859
|
+
onClick={[Function]}
|
|
860
|
+
>
|
|
861
|
+
<FormattedMessage
|
|
862
|
+
defaultMessage="Archived templates"
|
|
863
|
+
id="creatives.containersV2.Templates.archivedTemplates"
|
|
864
|
+
values={Object {}}
|
|
865
|
+
/>
|
|
866
|
+
</withItemHOC>
|
|
867
|
+
</CapMenuComponent>
|
|
868
|
+
}
|
|
869
|
+
placement="bottomRight"
|
|
870
|
+
trigger={
|
|
871
|
+
Array [
|
|
872
|
+
"click",
|
|
873
|
+
]
|
|
874
|
+
}
|
|
875
|
+
>
|
|
876
|
+
<CapButton
|
|
877
|
+
className="template-listing-more-btn"
|
|
878
|
+
type="flat"
|
|
879
|
+
>
|
|
880
|
+
<CapIcon
|
|
881
|
+
type="more"
|
|
882
|
+
/>
|
|
883
|
+
</CapButton>
|
|
884
|
+
</CapDropdownComponent>
|
|
885
|
+
</CapRow>
|
|
886
|
+
</CapRow>
|
|
835
887
|
</div>
|
|
836
888
|
</div>
|
|
837
889
|
</div>
|
|
@@ -1225,13 +1277,14 @@ exports[`Test Templates container Test max templates exceeded 1`] = `
|
|
|
1225
1277
|
</CapRow>
|
|
1226
1278
|
<Component />
|
|
1227
1279
|
<Component />
|
|
1228
|
-
<div
|
|
1229
|
-
className="template-listing-header-actions"
|
|
1230
|
-
/>
|
|
1231
1280
|
</div>
|
|
1232
1281
|
<div>
|
|
1233
|
-
<
|
|
1282
|
+
<CapRow
|
|
1283
|
+
align="middle"
|
|
1234
1284
|
className="action-container__create-row"
|
|
1285
|
+
gap={8}
|
|
1286
|
+
justify="space-between"
|
|
1287
|
+
type="flex"
|
|
1235
1288
|
>
|
|
1236
1289
|
<NoPermissionWrapper
|
|
1237
1290
|
allowed={false}
|
|
@@ -1245,7 +1298,44 @@ exports[`Test Templates container Test max templates exceeded 1`] = `
|
|
|
1245
1298
|
Create new
|
|
1246
1299
|
</CapButton>
|
|
1247
1300
|
</NoPermissionWrapper>
|
|
1248
|
-
|
|
1301
|
+
<CapRow
|
|
1302
|
+
align="middle"
|
|
1303
|
+
className="template-listing-header-actions"
|
|
1304
|
+
gap={8}
|
|
1305
|
+
type="flex"
|
|
1306
|
+
>
|
|
1307
|
+
<CapDropdownComponent
|
|
1308
|
+
overlay={
|
|
1309
|
+
<CapMenuComponent>
|
|
1310
|
+
<withItemHOC
|
|
1311
|
+
onClick={[Function]}
|
|
1312
|
+
>
|
|
1313
|
+
<FormattedMessage
|
|
1314
|
+
defaultMessage="Archived templates"
|
|
1315
|
+
id="creatives.containersV2.Templates.archivedTemplates"
|
|
1316
|
+
values={Object {}}
|
|
1317
|
+
/>
|
|
1318
|
+
</withItemHOC>
|
|
1319
|
+
</CapMenuComponent>
|
|
1320
|
+
}
|
|
1321
|
+
placement="bottomRight"
|
|
1322
|
+
trigger={
|
|
1323
|
+
Array [
|
|
1324
|
+
"click",
|
|
1325
|
+
]
|
|
1326
|
+
}
|
|
1327
|
+
>
|
|
1328
|
+
<CapButton
|
|
1329
|
+
className="template-listing-more-btn"
|
|
1330
|
+
type="flat"
|
|
1331
|
+
>
|
|
1332
|
+
<CapIcon
|
|
1333
|
+
type="more"
|
|
1334
|
+
/>
|
|
1335
|
+
</CapButton>
|
|
1336
|
+
</CapDropdownComponent>
|
|
1337
|
+
</CapRow>
|
|
1338
|
+
</CapRow>
|
|
1249
1339
|
</div>
|
|
1250
1340
|
</div>
|
|
1251
1341
|
</div>
|
|
@@ -1712,13 +1802,14 @@ exports[`Test Templates container Test max templates not exceeded 1`] = `
|
|
|
1712
1802
|
</CapRow>
|
|
1713
1803
|
<Component />
|
|
1714
1804
|
<Component />
|
|
1715
|
-
<div
|
|
1716
|
-
className="template-listing-header-actions"
|
|
1717
|
-
/>
|
|
1718
1805
|
</div>
|
|
1719
1806
|
<div>
|
|
1720
|
-
<
|
|
1807
|
+
<CapRow
|
|
1808
|
+
align="middle"
|
|
1721
1809
|
className="action-container__create-row"
|
|
1810
|
+
gap={8}
|
|
1811
|
+
justify="space-between"
|
|
1812
|
+
type="flex"
|
|
1722
1813
|
>
|
|
1723
1814
|
<NoPermissionWrapper
|
|
1724
1815
|
allowed={false}
|
|
@@ -1732,7 +1823,44 @@ exports[`Test Templates container Test max templates not exceeded 1`] = `
|
|
|
1732
1823
|
Create new
|
|
1733
1824
|
</CapButton>
|
|
1734
1825
|
</NoPermissionWrapper>
|
|
1735
|
-
|
|
1826
|
+
<CapRow
|
|
1827
|
+
align="middle"
|
|
1828
|
+
className="template-listing-header-actions"
|
|
1829
|
+
gap={8}
|
|
1830
|
+
type="flex"
|
|
1831
|
+
>
|
|
1832
|
+
<CapDropdownComponent
|
|
1833
|
+
overlay={
|
|
1834
|
+
<CapMenuComponent>
|
|
1835
|
+
<withItemHOC
|
|
1836
|
+
onClick={[Function]}
|
|
1837
|
+
>
|
|
1838
|
+
<FormattedMessage
|
|
1839
|
+
defaultMessage="Archived templates"
|
|
1840
|
+
id="creatives.containersV2.Templates.archivedTemplates"
|
|
1841
|
+
values={Object {}}
|
|
1842
|
+
/>
|
|
1843
|
+
</withItemHOC>
|
|
1844
|
+
</CapMenuComponent>
|
|
1845
|
+
}
|
|
1846
|
+
placement="bottomRight"
|
|
1847
|
+
trigger={
|
|
1848
|
+
Array [
|
|
1849
|
+
"click",
|
|
1850
|
+
]
|
|
1851
|
+
}
|
|
1852
|
+
>
|
|
1853
|
+
<CapButton
|
|
1854
|
+
className="template-listing-more-btn"
|
|
1855
|
+
type="flat"
|
|
1856
|
+
>
|
|
1857
|
+
<CapIcon
|
|
1858
|
+
type="more"
|
|
1859
|
+
/>
|
|
1860
|
+
</CapButton>
|
|
1861
|
+
</CapDropdownComponent>
|
|
1862
|
+
</CapRow>
|
|
1863
|
+
</CapRow>
|
|
1736
1864
|
</div>
|
|
1737
1865
|
</div>
|
|
1738
1866
|
</div>
|
|
@@ -2199,13 +2327,14 @@ exports[`Test Templates container Test max templates warning 1`] = `
|
|
|
2199
2327
|
</CapRow>
|
|
2200
2328
|
<Component />
|
|
2201
2329
|
<Component />
|
|
2202
|
-
<div
|
|
2203
|
-
className="template-listing-header-actions"
|
|
2204
|
-
/>
|
|
2205
2330
|
</div>
|
|
2206
2331
|
<div>
|
|
2207
|
-
<
|
|
2332
|
+
<CapRow
|
|
2333
|
+
align="middle"
|
|
2208
2334
|
className="action-container__create-row"
|
|
2335
|
+
gap={8}
|
|
2336
|
+
justify="space-between"
|
|
2337
|
+
type="flex"
|
|
2209
2338
|
>
|
|
2210
2339
|
<NoPermissionWrapper
|
|
2211
2340
|
allowed={false}
|
|
@@ -2219,7 +2348,44 @@ exports[`Test Templates container Test max templates warning 1`] = `
|
|
|
2219
2348
|
Create new
|
|
2220
2349
|
</CapButton>
|
|
2221
2350
|
</NoPermissionWrapper>
|
|
2222
|
-
|
|
2351
|
+
<CapRow
|
|
2352
|
+
align="middle"
|
|
2353
|
+
className="template-listing-header-actions"
|
|
2354
|
+
gap={8}
|
|
2355
|
+
type="flex"
|
|
2356
|
+
>
|
|
2357
|
+
<CapDropdownComponent
|
|
2358
|
+
overlay={
|
|
2359
|
+
<CapMenuComponent>
|
|
2360
|
+
<withItemHOC
|
|
2361
|
+
onClick={[Function]}
|
|
2362
|
+
>
|
|
2363
|
+
<FormattedMessage
|
|
2364
|
+
defaultMessage="Archived templates"
|
|
2365
|
+
id="creatives.containersV2.Templates.archivedTemplates"
|
|
2366
|
+
values={Object {}}
|
|
2367
|
+
/>
|
|
2368
|
+
</withItemHOC>
|
|
2369
|
+
</CapMenuComponent>
|
|
2370
|
+
}
|
|
2371
|
+
placement="bottomRight"
|
|
2372
|
+
trigger={
|
|
2373
|
+
Array [
|
|
2374
|
+
"click",
|
|
2375
|
+
]
|
|
2376
|
+
}
|
|
2377
|
+
>
|
|
2378
|
+
<CapButton
|
|
2379
|
+
className="template-listing-more-btn"
|
|
2380
|
+
type="flat"
|
|
2381
|
+
>
|
|
2382
|
+
<CapIcon
|
|
2383
|
+
type="more"
|
|
2384
|
+
/>
|
|
2385
|
+
</CapButton>
|
|
2386
|
+
</CapDropdownComponent>
|
|
2387
|
+
</CapRow>
|
|
2388
|
+
</CapRow>
|
|
2223
2389
|
</div>
|
|
2224
2390
|
</div>
|
|
2225
2391
|
</div>
|
|
@@ -2629,13 +2795,14 @@ exports[`Test Templates container Test removing all whatsapp filterss 1`] = `
|
|
|
2629
2795
|
</CapRow>
|
|
2630
2796
|
<Component />
|
|
2631
2797
|
<Component />
|
|
2632
|
-
<div
|
|
2633
|
-
className="template-listing-header-actions"
|
|
2634
|
-
/>
|
|
2635
2798
|
</div>
|
|
2636
2799
|
<div>
|
|
2637
|
-
<
|
|
2800
|
+
<CapRow
|
|
2801
|
+
align="middle"
|
|
2638
2802
|
className="action-container__create-row"
|
|
2803
|
+
gap={8}
|
|
2804
|
+
justify="space-between"
|
|
2805
|
+
type="flex"
|
|
2639
2806
|
>
|
|
2640
2807
|
<NoPermissionWrapper
|
|
2641
2808
|
allowed={false}
|
|
@@ -2652,7 +2819,13 @@ exports[`Test Templates container Test removing all whatsapp filterss 1`] = `
|
|
|
2652
2819
|
title="Create new template"
|
|
2653
2820
|
/>
|
|
2654
2821
|
</NoPermissionWrapper>
|
|
2655
|
-
|
|
2822
|
+
<CapRow
|
|
2823
|
+
align="middle"
|
|
2824
|
+
className="template-listing-header-actions"
|
|
2825
|
+
gap={8}
|
|
2826
|
+
type="flex"
|
|
2827
|
+
/>
|
|
2828
|
+
</CapRow>
|
|
2656
2829
|
</div>
|
|
2657
2830
|
</div>
|
|
2658
2831
|
</div>
|
|
@@ -2908,13 +3081,14 @@ exports[`Test Templates container Test removing all whatsapp filterss 2`] = `
|
|
|
2908
3081
|
</CapRow>
|
|
2909
3082
|
<Component />
|
|
2910
3083
|
<Component />
|
|
2911
|
-
<div
|
|
2912
|
-
className="template-listing-header-actions"
|
|
2913
|
-
/>
|
|
2914
3084
|
</div>
|
|
2915
3085
|
<div>
|
|
2916
|
-
<
|
|
3086
|
+
<CapRow
|
|
3087
|
+
align="middle"
|
|
2917
3088
|
className="action-container__create-row"
|
|
3089
|
+
gap={8}
|
|
3090
|
+
justify="space-between"
|
|
3091
|
+
type="flex"
|
|
2918
3092
|
>
|
|
2919
3093
|
<NoPermissionWrapper
|
|
2920
3094
|
allowed={false}
|
|
@@ -2931,7 +3105,13 @@ exports[`Test Templates container Test removing all whatsapp filterss 2`] = `
|
|
|
2931
3105
|
title="Create new template"
|
|
2932
3106
|
/>
|
|
2933
3107
|
</NoPermissionWrapper>
|
|
2934
|
-
|
|
3108
|
+
<CapRow
|
|
3109
|
+
align="middle"
|
|
3110
|
+
className="template-listing-header-actions"
|
|
3111
|
+
gap={8}
|
|
3112
|
+
type="flex"
|
|
3113
|
+
/>
|
|
3114
|
+
</CapRow>
|
|
2935
3115
|
</div>
|
|
2936
3116
|
</div>
|
|
2937
3117
|
</div>
|
|
@@ -3162,13 +3342,14 @@ exports[`Test Templates container Test removing single filter 1`] = `
|
|
|
3162
3342
|
</CapRow>
|
|
3163
3343
|
<Component />
|
|
3164
3344
|
<Component />
|
|
3165
|
-
<div
|
|
3166
|
-
className="template-listing-header-actions"
|
|
3167
|
-
/>
|
|
3168
3345
|
</div>
|
|
3169
3346
|
<div>
|
|
3170
|
-
<
|
|
3347
|
+
<CapRow
|
|
3348
|
+
align="middle"
|
|
3171
3349
|
className="action-container__create-row"
|
|
3350
|
+
gap={8}
|
|
3351
|
+
justify="space-between"
|
|
3352
|
+
type="flex"
|
|
3172
3353
|
>
|
|
3173
3354
|
<NoPermissionWrapper
|
|
3174
3355
|
allowed={false}
|
|
@@ -3185,7 +3366,13 @@ exports[`Test Templates container Test removing single filter 1`] = `
|
|
|
3185
3366
|
title="Create new template"
|
|
3186
3367
|
/>
|
|
3187
3368
|
</NoPermissionWrapper>
|
|
3188
|
-
|
|
3369
|
+
<CapRow
|
|
3370
|
+
align="middle"
|
|
3371
|
+
className="template-listing-header-actions"
|
|
3372
|
+
gap={8}
|
|
3373
|
+
type="flex"
|
|
3374
|
+
/>
|
|
3375
|
+
</CapRow>
|
|
3189
3376
|
</div>
|
|
3190
3377
|
</div>
|
|
3191
3378
|
</div>
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { shallowWithIntl } from '../../../helpers/intl-enzym-test-helpers';
|
|
3
|
+
import { Templates } from '../index';
|
|
4
|
+
import mockdata from '../../mockdata';
|
|
5
|
+
import { DEFAULT_ARCHIVAL_LIMIT } from '../constants';
|
|
6
|
+
import { CREATIVE_CREATE, CREATIVE_DELETE, CREATIVE_EDIT } from '../../App/constants';
|
|
7
|
+
|
|
8
|
+
const { whatsappTemplates: TemplatesProp } = mockdata;
|
|
9
|
+
|
|
10
|
+
jest.mock('@capillarytech/cap-ui-library', () => ({
|
|
11
|
+
...jest.requireActual('@capillarytech/cap-ui-library'),
|
|
12
|
+
CapNotification: { error: jest.fn(), success: jest.fn(), warning: jest.fn() },
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
jest.mock('../../CreativesContainer', () => ({
|
|
16
|
+
__esModule: true,
|
|
17
|
+
default: () => null,
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
const buildTemplateIds = count => Array.from({ length: count }, (_, index) => `template_${index}`);
|
|
21
|
+
|
|
22
|
+
// Walks a rendered element/plain-object tree (cards are never mounted) for a node by className.
|
|
23
|
+
const findByClassName = (node, className, seen = new Set()) => {
|
|
24
|
+
if (!node || typeof node !== 'object' || seen.has(node)) return null;
|
|
25
|
+
seen.add(node);
|
|
26
|
+
if (Array.isArray(node)) {
|
|
27
|
+
return node.reduce((found, child) => found || findByClassName(child, className, seen), null);
|
|
28
|
+
}
|
|
29
|
+
if (node.props && typeof node.props.className === 'string' && node.props.className.includes(className)) {
|
|
30
|
+
return node;
|
|
31
|
+
}
|
|
32
|
+
return Object.values(node.props || node).reduce(
|
|
33
|
+
(found, value) => found || findByClassName(value, className, seen),
|
|
34
|
+
null,
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
describe('Templates archival (enabled by default)', () => {
|
|
39
|
+
const actions = {
|
|
40
|
+
getWeCrmAccounts: jest.fn(),
|
|
41
|
+
setChannelAccount: jest.fn(),
|
|
42
|
+
getAllTemplates: jest.fn(),
|
|
43
|
+
getUserList: jest.fn(),
|
|
44
|
+
getSenderDetails: jest.fn(),
|
|
45
|
+
resetTemplate: jest.fn(),
|
|
46
|
+
setArchivedMode: jest.fn(),
|
|
47
|
+
clearTemplateSelection: jest.fn(),
|
|
48
|
+
toggleTemplateSelection: jest.fn(),
|
|
49
|
+
bulkArchiveTemplates: jest.fn(),
|
|
50
|
+
bulkUnarchiveTemplates: jest.fn(),
|
|
51
|
+
archiveTemplate: jest.fn(),
|
|
52
|
+
unarchiveTemplate: jest.fn(),
|
|
53
|
+
};
|
|
54
|
+
let renderedComponent;
|
|
55
|
+
|
|
56
|
+
const renderTemplates = ({ channel = 'email', isFullMode = true, templatesProps = {} } = {}) => {
|
|
57
|
+
renderedComponent = shallowWithIntl(
|
|
58
|
+
<Templates
|
|
59
|
+
route={{ name: channel }}
|
|
60
|
+
cap={{ user: { accessiblePermissions: [CREATIVE_CREATE, CREATIVE_EDIT, CREATIVE_DELETE] } }}
|
|
61
|
+
Templates={{ ...TemplatesProp, ...templatesProps }}
|
|
62
|
+
actions={actions}
|
|
63
|
+
location={{ pathname: `/${channel}`, query: {}, search: '' }}
|
|
64
|
+
EmailCreate={{ duplicateTemplateInProgress: false }}
|
|
65
|
+
isFullMode={isFullMode}
|
|
66
|
+
intl={{ formatMessage: ({ defaultMessage }) => defaultMessage }}
|
|
67
|
+
/>,
|
|
68
|
+
);
|
|
69
|
+
return renderedComponent;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const getCardList = (templates, channel = 'email') =>
|
|
73
|
+
renderedComponent.instance().getTemplateDataForGrid({
|
|
74
|
+
templates,
|
|
75
|
+
handlers: { handlePreviewClick: jest.fn(), handleEditClick: jest.fn() },
|
|
76
|
+
filterContent: null,
|
|
77
|
+
channel,
|
|
78
|
+
isLoading: false,
|
|
79
|
+
isInitialLoading: false,
|
|
80
|
+
loadingTip: '',
|
|
81
|
+
previewTemplateId: null,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
beforeEach(() => {
|
|
85
|
+
jest.clearAllMocks();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('bulk selection bar', () => {
|
|
89
|
+
const getBulkArchiveButton = () =>
|
|
90
|
+
renderedComponent
|
|
91
|
+
.find('.bulk-selection-bar')
|
|
92
|
+
.find('.archive-template-button');
|
|
93
|
+
|
|
94
|
+
it('renders without any feature flag once templates are selected', () => {
|
|
95
|
+
renderTemplates({ templatesProps: { selectedTemplateIds: buildTemplateIds(2) } });
|
|
96
|
+
expect(renderedComponent.find('.bulk-selection-bar')).toHaveLength(1);
|
|
97
|
+
expect(getBulkArchiveButton().prop('disabled')).toBe(false);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('is not rendered when nothing is selected', () => {
|
|
101
|
+
renderTemplates({ templatesProps: { selectedTemplateIds: [] } });
|
|
102
|
+
expect(renderedComponent.find('.bulk-selection-bar')).toHaveLength(0);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('is not rendered outside full mode', () => {
|
|
106
|
+
renderTemplates({ isFullMode: false, templatesProps: { selectedTemplateIds: buildTemplateIds(2) } });
|
|
107
|
+
expect(renderedComponent.find('.bulk-selection-bar')).toHaveLength(0);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('keeps the action enabled at exactly the archival limit', () => {
|
|
111
|
+
renderTemplates({ templatesProps: { selectedTemplateIds: buildTemplateIds(DEFAULT_ARCHIVAL_LIMIT) } });
|
|
112
|
+
expect(getBulkArchiveButton().prop('disabled')).toBe(false);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('disables the action and shows the limit message above the archival limit', () => {
|
|
116
|
+
renderTemplates({ templatesProps: { selectedTemplateIds: buildTemplateIds(DEFAULT_ARCHIVAL_LIMIT + 1) } });
|
|
117
|
+
expect(getBulkArchiveButton().prop('disabled')).toBe(true);
|
|
118
|
+
const tooltip = renderedComponent.find('.bulk-selection-bar').find('CapTooltip').first();
|
|
119
|
+
expect(tooltip.prop('title')).toContain('Archive limit exceeded');
|
|
120
|
+
expect(tooltip.prop('title')).toContain(String(DEFAULT_ARCHIVAL_LIMIT));
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('triggers bulk unarchive for the selection in archived mode', () => {
|
|
124
|
+
const selectedIds = buildTemplateIds(2);
|
|
125
|
+
renderTemplates({ templatesProps: { selectedTemplateIds: selectedIds, isArchivedMode: true } });
|
|
126
|
+
const handleBulkArchiveAction = jest
|
|
127
|
+
.spyOn(renderedComponent.instance(), 'handleBulkArchiveAction')
|
|
128
|
+
.mockImplementation(() => {});
|
|
129
|
+
getBulkArchiveButton().prop('onClick')();
|
|
130
|
+
expect(handleBulkArchiveAction).toHaveBeenCalledWith({ ids: selectedIds, count: 2, isUnarchive: true });
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe('listing toolbar', () => {
|
|
135
|
+
it('shows the create button, followed by the archived-templates kebab, when nothing is selected', () => {
|
|
136
|
+
renderTemplates({ templatesProps: { selectedTemplateIds: [] } });
|
|
137
|
+
const createRow = renderedComponent.find('.action-container__create-row');
|
|
138
|
+
expect(createRow.find('.create-new-email')).toHaveLength(1);
|
|
139
|
+
expect(createRow.find('.template-listing-more-btn')).toHaveLength(1);
|
|
140
|
+
expect(renderedComponent.find('.action-container__toolbar-row .template-listing-more-btn')).toHaveLength(0);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('hides the create button and the kebab while templates are selected for archival', () => {
|
|
144
|
+
renderTemplates({ templatesProps: { selectedTemplateIds: buildTemplateIds(2) } });
|
|
145
|
+
expect(renderedComponent.find('.create-new-email')).toHaveLength(0);
|
|
146
|
+
expect(renderedComponent.find('.template-listing-more-btn')).toHaveLength(0);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('hides the create button while templates are selected for unarchival', () => {
|
|
150
|
+
renderTemplates({ templatesProps: { selectedTemplateIds: buildTemplateIds(2), isArchivedMode: true } });
|
|
151
|
+
expect(renderedComponent.find('.create-new-email')).toHaveLength(0);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe('handleBulkArchiveAction', () => {
|
|
156
|
+
it('opens the confirm modal and bulk archives the selected ids on confirm', () => {
|
|
157
|
+
renderTemplates();
|
|
158
|
+
const instance = renderedComponent.instance();
|
|
159
|
+
const showArchiveConfirm = jest.spyOn(instance, 'showArchiveConfirm').mockImplementation(() => {});
|
|
160
|
+
instance.handleBulkArchiveAction({ ids: ['a', 'b'], count: 2 });
|
|
161
|
+
expect(showArchiveConfirm).toHaveBeenCalledWith(expect.objectContaining({ count: 2, isUnarchive: false }));
|
|
162
|
+
showArchiveConfirm.mock.calls[0][0].onConfirm();
|
|
163
|
+
expect(actions.bulkArchiveTemplates).toHaveBeenCalledWith('Email', ['a', 'b'], expect.any(Function));
|
|
164
|
+
expect(actions.bulkUnarchiveTemplates).not.toHaveBeenCalled();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('bulk unarchives when isUnarchive is set', () => {
|
|
168
|
+
renderTemplates();
|
|
169
|
+
const instance = renderedComponent.instance();
|
|
170
|
+
const showArchiveConfirm = jest.spyOn(instance, 'showArchiveConfirm').mockImplementation(() => {});
|
|
171
|
+
instance.handleBulkArchiveAction({ ids: ['a'], count: 1, isUnarchive: true });
|
|
172
|
+
showArchiveConfirm.mock.calls[0][0].onConfirm();
|
|
173
|
+
expect(actions.bulkUnarchiveTemplates).toHaveBeenCalledWith('Email', ['a'], expect.any(Function));
|
|
174
|
+
expect(actions.bulkArchiveTemplates).not.toHaveBeenCalled();
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
describe('single template card', () => {
|
|
179
|
+
const activeTemplate = { _id: 'tpl_1', name: 'Active template', versions: { base: {} } };
|
|
180
|
+
const archivedTemplate = { _id: 'tpl_2', name: 'Archived template', isArchived: true, versions: { base: {} } };
|
|
181
|
+
|
|
182
|
+
it('shows the archive menu action for an active template without any feature flag', () => {
|
|
183
|
+
renderTemplates();
|
|
184
|
+
const cardList = getCardList([activeTemplate]);
|
|
185
|
+
expect(findByClassName(cardList, 'archive-email')).not.toBeNull();
|
|
186
|
+
expect(findByClassName(cardList, 'unarchive-email')).toBeNull();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('shows the unarchive menu action for an archived template', () => {
|
|
190
|
+
renderTemplates();
|
|
191
|
+
const cardList = getCardList([archivedTemplate]);
|
|
192
|
+
expect(findByClassName(cardList, 'unarchive-email')).not.toBeNull();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('shows the archived tag and unarchive popover action for an archived template', () => {
|
|
196
|
+
renderTemplates({ channel: 'sms' });
|
|
197
|
+
const cardList = renderedComponent.instance().populateTemplatesList([archivedTemplate], false, false);
|
|
198
|
+
expect(findByClassName(cardList, 'archived-tag')).not.toBeNull();
|
|
199
|
+
expect(findByClassName(cardList, 'popover-archive-action')).not.toBeNull();
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('shows no archived tag for an active template', () => {
|
|
203
|
+
renderTemplates({ channel: 'sms' });
|
|
204
|
+
const cardList = renderedComponent.instance().populateTemplatesList([activeTemplate], false, false);
|
|
205
|
+
expect(findByClassName(cardList, 'archived-tag')).toBeNull();
|
|
206
|
+
expect(findByClassName(cardList, 'popover-archive-action')).not.toBeNull();
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('hides the archive actions outside full mode', () => {
|
|
210
|
+
renderTemplates({ isFullMode: false });
|
|
211
|
+
const cardList = getCardList([activeTemplate]);
|
|
212
|
+
expect(findByClassName(cardList, 'archive-email')).toBeNull();
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('archives a single template through the confirm modal', () => {
|
|
216
|
+
renderTemplates();
|
|
217
|
+
const instance = renderedComponent.instance();
|
|
218
|
+
const showArchiveConfirm = jest.spyOn(instance, 'showArchiveConfirm').mockImplementation(() => {});
|
|
219
|
+
instance.handleTemplateArchiveAction({ templateId: 'tpl_1', templateName: 'Active template' });
|
|
220
|
+
expect(showArchiveConfirm).toHaveBeenCalledTimes(1);
|
|
221
|
+
showArchiveConfirm.mock.calls[0][0].onConfirm();
|
|
222
|
+
expect(actions.archiveTemplate).toHaveBeenCalled();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('unarchives a single template through the confirm modal', () => {
|
|
226
|
+
renderTemplates();
|
|
227
|
+
const instance = renderedComponent.instance();
|
|
228
|
+
const showArchiveConfirm = jest.spyOn(instance, 'showArchiveConfirm').mockImplementation(() => {});
|
|
229
|
+
instance.handleTemplateArchiveAction({ templateId: 'tpl_2', templateName: 'Archived template', isUnarchive: true });
|
|
230
|
+
showArchiveConfirm.mock.calls[0][0].onConfirm();
|
|
231
|
+
expect(actions.unarchiveTemplate).toHaveBeenCalled();
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
describe('isChannelArchiveEligible', () => {
|
|
236
|
+
it('does not allow archiving Zalo templates', () => {
|
|
237
|
+
renderTemplates();
|
|
238
|
+
expect(renderedComponent.instance().isChannelArchiveEligible('ZALO')).toBe(false);
|
|
239
|
+
expect(renderedComponent.instance().isChannelArchiveEligible('SMS')).toBe(true);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
});
|