@capillarytech/creatives-library 8.0.353-alpha.4 → 8.0.353-alpha.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 +29 -0
- package/package.json +1 -1
- package/services/tests/api.test.js +35 -20
- package/utils/commonUtils.js +19 -1
- package/utils/rcsPayloadUtils.js +92 -0
- package/utils/templateVarUtils.js +201 -0
- package/utils/tests/rcsPayloadUtils.test.js +226 -0
- package/utils/tests/templateVarUtils.test.js +204 -0
- package/v2Components/CapActionButton/constants.js +7 -0
- package/v2Components/CapActionButton/index.js +166 -108
- package/v2Components/CapActionButton/index.scss +157 -6
- package/v2Components/CapActionButton/messages.js +19 -3
- package/v2Components/CapActionButton/tests/index.test.js +41 -17
- package/v2Components/CapTagList/index.js +10 -0
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +72 -49
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +213 -21
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +85 -10
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +79 -11
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +157 -15
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +346 -76
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +133 -4
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
- package/v2Components/CommonTestAndPreview/constants.js +38 -2
- package/v2Components/CommonTestAndPreview/index.js +691 -186
- package/v2Components/CommonTestAndPreview/messages.js +45 -3
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
- package/v2Components/CommonTestAndPreview/sagas.js +25 -6
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +308 -284
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +231 -65
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
- package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +34 -13
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
- package/v2Components/CommonTestAndPreview/tests/index.test.js +132 -4
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +36 -26
- package/v2Components/FormBuilder/index.js +74 -166
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +91 -0
- package/v2Components/SmsFallback/constants.js +73 -0
- package/v2Components/SmsFallback/index.js +956 -0
- package/v2Components/SmsFallback/index.scss +265 -0
- package/v2Components/SmsFallback/messages.js +78 -0
- package/v2Components/SmsFallback/smsFallbackUtils.js +119 -0
- package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
- package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
- package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
- package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +223 -0
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +309 -0
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
- package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
- package/v2Components/TemplatePreview/_templatePreview.scss +38 -23
- package/v2Components/TemplatePreview/constants.js +2 -0
- package/v2Components/TemplatePreview/index.js +143 -31
- package/v2Components/TemplatePreview/tests/index.test.js +142 -0
- package/v2Components/TestAndPreviewSlidebox/index.js +13 -1
- package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
- package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
- package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
- package/v2Components/VarSegmentMessageEditor/index.js +125 -0
- package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
- package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +43 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +36 -4
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +10 -1
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
- package/v2Containers/CreativesContainer/constants.js +9 -0
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +79 -0
- package/v2Containers/CreativesContainer/index.js +346 -163
- package/v2Containers/CreativesContainer/index.scss +51 -1
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +78 -34
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +79 -16
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +357 -98
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
- package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
- package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
- package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
- package/v2Containers/Email/index.js +1 -1
- package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
- package/v2Containers/Rcs/constants.js +119 -10
- package/v2Containers/Rcs/index.js +2445 -813
- package/v2Containers/Rcs/index.scss +280 -8
- package/v2Containers/Rcs/messages.js +34 -3
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +225 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +98018 -70073
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
- package/v2Containers/Rcs/tests/index.test.js +152 -121
- package/v2Containers/Rcs/tests/mockData.js +38 -0
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +318 -0
- package/v2Containers/Rcs/tests/utils.test.js +646 -30
- package/v2Containers/Rcs/utils.js +478 -11
- package/v2Containers/Sms/Create/index.js +106 -40
- package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
- package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
- package/v2Containers/SmsTrai/Create/index.js +9 -4
- package/v2Containers/SmsTrai/Edit/constants.js +2 -0
- package/v2Containers/SmsTrai/Edit/index.js +640 -130
- package/v2Containers/SmsTrai/Edit/index.scss +121 -0
- package/v2Containers/SmsTrai/Edit/messages.js +14 -4
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4328 -2375
- package/v2Containers/SmsWrapper/index.js +37 -8
- package/v2Containers/TagList/index.js +6 -0
- package/v2Containers/Templates/TemplatesActionBar.js +101 -0
- package/v2Containers/Templates/_templates.scss +166 -9
- package/v2Containers/Templates/actions.js +11 -0
- package/v2Containers/Templates/constants.js +2 -0
- package/v2Containers/Templates/index.js +120 -52
- package/v2Containers/Templates/sagas.js +56 -12
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1062 -1017
- package/v2Containers/Templates/tests/sagas.test.js +199 -16
- package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
- package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
- package/v2Containers/TemplatesV2/index.js +86 -23
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
- package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
- package/v2Containers/Whatsapp/index.js +3 -20
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
|
@@ -122,7 +122,7 @@ import { INAPP_LAYOUT_DETAILS, INAPP_MESSAGE_LAYOUT_TYPES } from '../InApp/const
|
|
|
122
122
|
import { ZALO_STATUS_OPTIONS, ZALO_STATUSES } from '../Zalo/constants';
|
|
123
123
|
import { getWhatsappContent, getWhatsappStatus, getWhatsappCategory, getWhatsappCta, getWhatsappQuickReply, getWhatsappAutoFill, getWhatsappCarouselButtonView } from '../Whatsapp/utils';
|
|
124
124
|
import { getRCSContent } from '../Rcs/utils';
|
|
125
|
-
import {RCS_STATUSES} from '../Rcs/constants';
|
|
125
|
+
import { RCS_STATUSES, HOST_INFOBIP } from '../Rcs/constants';
|
|
126
126
|
import zaloMessages from '../Zalo/messages';
|
|
127
127
|
import rcsMessages from '../Rcs/messages';
|
|
128
128
|
import inAppMessages from '../InApp/messages';
|
|
@@ -460,7 +460,13 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
460
460
|
if (this.props.location.query.type === 'embedded') {
|
|
461
461
|
this.props.actions.resetAccount();
|
|
462
462
|
}
|
|
463
|
-
|
|
463
|
+
// When using local templates (e.g. SMS fallback selector), do not fetch from API or we overwrite global store and break background RCS list
|
|
464
|
+
const useLocalTemplates = get(
|
|
465
|
+
this.props,
|
|
466
|
+
'localTemplatesConfig.useLocalTemplates',
|
|
467
|
+
get(this.props, 'useLocalTemplates', false),
|
|
468
|
+
);
|
|
469
|
+
if (!useLocalTemplates && ['line', VIBER_CHANNEL, FACEBOOK_CHANNEL, 'sms', 'email', 'ebill'].includes((this.state.channel || '').toLowerCase())) {
|
|
464
470
|
const queryParams = {
|
|
465
471
|
// name: this.state.searchText,
|
|
466
472
|
// sortBy: this.state.sortBy,
|
|
@@ -1006,8 +1012,16 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
1006
1012
|
|
|
1007
1013
|
componentWillUnmount() {
|
|
1008
1014
|
window.removeEventListener("message", this.handleFrameTasks);
|
|
1009
|
-
|
|
1010
|
-
|
|
1015
|
+
// When using local templates (e.g. SMS fallback selector), do not clear global store or background RCS list is wiped
|
|
1016
|
+
const useLocalTemplates = get(
|
|
1017
|
+
this.props,
|
|
1018
|
+
'localTemplatesConfig.useLocalTemplates',
|
|
1019
|
+
get(this.props, 'useLocalTemplates', false),
|
|
1020
|
+
);
|
|
1021
|
+
if (!useLocalTemplates) {
|
|
1022
|
+
this.props.actions.resetTemplateStoreData();
|
|
1023
|
+
this.props.globalActions.clearMetaEntities();
|
|
1024
|
+
}
|
|
1011
1025
|
// Clear any pending timeouts to prevent memory leaks
|
|
1012
1026
|
if (this._clearEditTimeout) {
|
|
1013
1027
|
clearTimeout(this._clearEditTimeout);
|
|
@@ -1797,12 +1811,20 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
1797
1811
|
}
|
|
1798
1812
|
|
|
1799
1813
|
filterRcsTemplates = (templates) => {
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1814
|
+
const selectedRcsAccountName = this.props?.Templates?.selectedRcsAccount?.name || '';
|
|
1815
|
+
const hostName = this.state?.hostName;
|
|
1816
|
+
let nextTemplates = templates || [];
|
|
1817
|
+
if (selectedRcsAccountName) {
|
|
1818
|
+
nextTemplates = nextTemplates.filter(
|
|
1819
|
+
(t) => get(t, 'versions.base.content.RCS.rcsContent.accountName', '') === selectedRcsAccountName
|
|
1820
|
+
);
|
|
1804
1821
|
}
|
|
1805
|
-
|
|
1822
|
+
if (!this.props.isFullMode && hostName !== HOST_INFOBIP) {
|
|
1823
|
+
return nextTemplates.filter(
|
|
1824
|
+
(t) => get(t, 'versions.base.content.RCS.rcsContent.cardContent[0].Status', 'unavailable') === RCS_STATUSES.approved
|
|
1825
|
+
);
|
|
1826
|
+
}
|
|
1827
|
+
return nextTemplates;
|
|
1806
1828
|
}
|
|
1807
1829
|
|
|
1808
1830
|
filterZaloTemplates = (templates) => {
|
|
@@ -1992,7 +2014,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
1992
2014
|
style={{ marginRight: "16px" }}
|
|
1993
2015
|
type="eye"
|
|
1994
2016
|
onClick={() => {
|
|
1995
|
-
if (!this.props.isFullMode || this.props.isDltFromRcs) {
|
|
2017
|
+
if (!this.props.isFullMode || this.props.isDltFromRcs || this.props.isSmsFallbackFromRcs) {
|
|
1996
2018
|
if (!get(template, "versions.base.content.zalo.previewUrl", "")) {
|
|
1997
2019
|
this.setState({ zaloPreviewItemId: template?._id });
|
|
1998
2020
|
}
|
|
@@ -2474,13 +2496,14 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2474
2496
|
<CapRow type="flex" align="middle">
|
|
2475
2497
|
{isCardArchiveEligible && this.renderCardSelectionCheckbox({ templateId: template._id, selectedIds: selectedIdsArrayForCard, isDisabled: isAnyArchiveInProgress })}
|
|
2476
2498
|
<CapLabel className="whatsapp-rcs-template-name">{name}</CapLabel>
|
|
2477
|
-
<CapRow type="flex" align="middle" className="rcs-status-container zalo-status-color">
|
|
2499
|
+
{this.state.hostName !== HOST_INFOBIP && <CapRow type="flex" align="middle" className="rcs-status-container zalo-status-color">
|
|
2478
2500
|
<CapStatus
|
|
2479
2501
|
type={statusDisplay}
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2502
|
+
text={statusDisplay && this.props.intl.formatMessage(rcsMessages?.[`${statusDisplay}_STATUS`])}
|
|
2503
|
+
labelType="label3"
|
|
2504
|
+
/>
|
|
2505
|
+
</CapRow>
|
|
2506
|
+
}
|
|
2484
2507
|
</CapRow>
|
|
2485
2508
|
);
|
|
2486
2509
|
|
|
@@ -3056,6 +3079,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
3056
3079
|
let routeParams = {};
|
|
3057
3080
|
const {fbAdManager} = this.props;
|
|
3058
3081
|
const isLibraryMode = this.isEnabledInLibraryModule("callCreateFromProps");
|
|
3082
|
+
|
|
3059
3083
|
if (!isLibraryMode) {
|
|
3060
3084
|
timeTracker.startTimer(CHANNEL_CREATE_TRACK_MAPPING[channel]);
|
|
3061
3085
|
}
|
|
@@ -3405,6 +3429,13 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
3405
3429
|
this.setState({modeType});
|
|
3406
3430
|
}
|
|
3407
3431
|
const { _id: id } = template;
|
|
3432
|
+
const {
|
|
3433
|
+
localTemplatesConfig,
|
|
3434
|
+
fbAdManager,
|
|
3435
|
+
isDltFromRcs,
|
|
3436
|
+
isSmsFallbackFromRcs,
|
|
3437
|
+
onSelectTemplate,
|
|
3438
|
+
} = this.props;
|
|
3408
3439
|
const type = this.props.location.query.type;
|
|
3409
3440
|
const module = this.props.location.query.module;
|
|
3410
3441
|
const isLanguageSupport = (this.props.location.query.isLanguageSupport) ? this.props.location.query.isLanguageSupport : false;
|
|
@@ -3490,10 +3521,12 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
3490
3521
|
}
|
|
3491
3522
|
if (this.isEnabledInLibraryModule("callSelectFromProps")) {
|
|
3492
3523
|
let data = id;
|
|
3493
|
-
if (
|
|
3524
|
+
if (localTemplatesConfig?.useLocalTemplates) {
|
|
3525
|
+
data = template;
|
|
3526
|
+
} else if (fbAdManager || isDltFromRcs || isSmsFallbackFromRcs) {
|
|
3494
3527
|
data = this.selectTemplate(id);
|
|
3495
3528
|
}
|
|
3496
|
-
|
|
3529
|
+
onSelectTemplate(data, fbAdManager);
|
|
3497
3530
|
} else {
|
|
3498
3531
|
timeTracker.startTimer(CHANNEL_EDIT_TRACK_MAPPING[this.state.channel.toLowerCase()]);
|
|
3499
3532
|
if (this.state.channel.toLowerCase() === 'ebill') {
|
|
@@ -4452,9 +4485,14 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4452
4485
|
const isWechatEmbedded = !this.props.isFullMode && channel.toUpperCase() === WECHAT;
|
|
4453
4486
|
const channelLowerCase = (channel || '').toLowerCase();
|
|
4454
4487
|
const isTraiDltFeature = this.checkDLTfeatureEnable();
|
|
4455
|
-
|
|
4456
4488
|
const createButton =
|
|
4457
|
-
(
|
|
4489
|
+
(
|
|
4490
|
+
(
|
|
4491
|
+
channelLowerCase === WHATSAPP_LOWERCASE
|
|
4492
|
+
|| channelLowerCase === RCS_LOWERCASE
|
|
4493
|
+
)
|
|
4494
|
+
&& !this.props.isFullMode
|
|
4495
|
+
)
|
|
4458
4496
|
? (
|
|
4459
4497
|
<CapLink
|
|
4460
4498
|
onClick={this.openCreativesFullMode}
|
|
@@ -4486,17 +4524,22 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4486
4524
|
const _renderSelectedIdsArray = _renderSelectedIds && typeof _renderSelectedIds.toJS === 'function' ? _renderSelectedIds.toJS() : (Array.isArray(_renderSelectedIds) ? _renderSelectedIds : []);
|
|
4487
4525
|
const _renderHasSelection = _isArchivalEnabled && this.props.isFullMode && _renderSelectedIdsArray.length > 0;
|
|
4488
4526
|
|
|
4489
|
-
const
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4527
|
+
const useLocalTemplates = this.props.localTemplatesConfig?.useLocalTemplates;
|
|
4528
|
+
const builtFilterContent = ((isfilterContentVisisble || [WECHAT, MOBILE_PUSH, INAPP].includes(this.state.channel.toUpperCase())) && (
|
|
4529
|
+
<div className="action-container">
|
|
4530
|
+
<div className="action-container__toolbar-row">
|
|
4531
|
+
{isfilterContentVisisble ? (
|
|
4532
|
+
<CapInput.Search
|
|
4533
|
+
className="search-text"
|
|
4534
|
+
placeholder={this.props.intl.formatMessage(messages.searchText)}
|
|
4535
|
+
value={this.state.searchText}
|
|
4536
|
+
onChange={(e) => this.searchTemplate(e.target.value, this.state.channel)}
|
|
4537
|
+
onSearch={() => this.searchTemplate(this.state.searchText, this.state.channel)}
|
|
4538
|
+
onClear={() => this.searchTemplate('', this.state.channel)}
|
|
4539
|
+
onScroll={(e) => e.stopPropagation()}
|
|
4540
|
+
disabled={this.checkSearchDisabled()}
|
|
4541
|
+
/>
|
|
4542
|
+
) : null}
|
|
4500
4543
|
{
|
|
4501
4544
|
channel.toUpperCase() === WECHAT && <CapRadio.CapRadioGroup className="wechat-filters" defaultValue={wechatFilter} onChange={this.setWechatFilter}>
|
|
4502
4545
|
<CapRadio.Button value={WECHAT_FILTERS.ALL}><CapLabel type="label2">
|
|
@@ -4643,16 +4686,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4643
4686
|
)
|
|
4644
4687
|
}
|
|
4645
4688
|
<div className="template-listing-header-actions">
|
|
4646
|
-
{!_isArchivedMode && !_renderHasSelection && (
|
|
4647
|
-
this.state?.channel?.toLowerCase() === WHATSAPP_LOWERCASE && (isWhatsappCountExeeded) ? (
|
|
4648
|
-
<CapTooltip title={whatsappCountExceedText}>
|
|
4649
|
-
<div className="button-disabled-tooltip-wrapper">
|
|
4650
|
-
{createButton}
|
|
4651
|
-
</div>
|
|
4652
|
-
</CapTooltip>
|
|
4653
|
-
)
|
|
4654
|
-
: isfilterContentVisisble && !isWechatEmbedded && !this.props.isDltFromRcs && createButton
|
|
4655
|
-
)}
|
|
4656
4689
|
{/* More (⋯) menu: full mode only, not archived mode, not Zalo (no archive support), not when selection active, archive flag enabled */}
|
|
4657
4690
|
{commonUtil.hasCreativesArchivalEnabled() && !_isArchivedMode && !_renderHasSelection && this.props.isFullMode && this.props.location.query.type !== EMBEDDED && channelLowerCase !== ZALO_LOWERCASE && (
|
|
4658
4691
|
<CapDropdown
|
|
@@ -4675,7 +4708,28 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4675
4708
|
</CapDropdown>
|
|
4676
4709
|
)}
|
|
4677
4710
|
</div>
|
|
4678
|
-
</div>
|
|
4711
|
+
</div>
|
|
4712
|
+
<div>
|
|
4713
|
+
<div className="action-container__create-row">
|
|
4714
|
+
{
|
|
4715
|
+
isfilterContentVisisble && !isWechatEmbedded && !this.props.isDltFromRcs && !this.props.isSmsFallbackFromRcs && (
|
|
4716
|
+
this.state?.channel?.toLowerCase() === WHATSAPP_LOWERCASE && isWhatsappCountExeeded ? (
|
|
4717
|
+
<CapTooltip title={whatsappCountExceedText}>
|
|
4718
|
+
<div className="button-disabled-tooltip-wrapper">
|
|
4719
|
+
{createButton}
|
|
4720
|
+
</div>
|
|
4721
|
+
</CapTooltip>
|
|
4722
|
+
) : createButton
|
|
4723
|
+
)
|
|
4724
|
+
}
|
|
4725
|
+
</div>
|
|
4726
|
+
</div>
|
|
4727
|
+
</div>
|
|
4728
|
+
));
|
|
4729
|
+
const localTemplatesFilterContent = get(this.props, 'localTemplatesConfig.localTemplatesFilterContent', null);
|
|
4730
|
+
const filterContent = (useLocalTemplates && localTemplatesFilterContent) != null
|
|
4731
|
+
? localTemplatesFilterContent
|
|
4732
|
+
: builtFilterContent;
|
|
4679
4733
|
let htmlPreviewContent = "";
|
|
4680
4734
|
if (this.state.channel.toLowerCase() === 'ebill') {
|
|
4681
4735
|
htmlPreviewContent = this.state.previewTemplate && this.state.previewTemplate.versions && this.state.previewTemplate.versions.base && this.state.previewTemplate.versions.base['ebill-editor'];
|
|
@@ -4685,7 +4739,10 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4685
4739
|
|
|
4686
4740
|
|
|
4687
4741
|
const creativesParams = this.getCreativesParams();
|
|
4688
|
-
const templates =
|
|
4742
|
+
const templates = useLocalTemplates
|
|
4743
|
+
? (this.props.localTemplatesConfig?.localTemplates || [])
|
|
4744
|
+
: (this.props.TemplatesList || []);
|
|
4745
|
+
const isLoadingWhenLocal = useLocalTemplates && !!this.props.localTemplatesConfig?.localTemplatesLoading;
|
|
4689
4746
|
const {route} = this.props;
|
|
4690
4747
|
const loadingTipMap = {
|
|
4691
4748
|
sendingFile: 'uploadingFile',
|
|
@@ -4700,9 +4757,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4700
4757
|
(deleteRcsTemplateInProgress && 'deletingTemplate') ||
|
|
4701
4758
|
(this.props.EmailCreate.duplicateTemplateInProgress && 'duplicatingTemplate');
|
|
4702
4759
|
|
|
4703
|
-
const loadingTip =
|
|
4760
|
+
const loadingTip = useLocalTemplates && this.props.localTemplatesConfig?.localTemplatesLoadingTip
|
|
4761
|
+
? this.props.localTemplatesConfig.localTemplatesLoadingTip
|
|
4762
|
+
: (messages[loadingTipIntl] ? this.props.intl.formatMessage(messages[loadingTipIntl]) : this.props.intl.formatMessage(messages.gettingAllTemplates));
|
|
4704
4763
|
const showNoTemplatesFoundZalo = this.state.channel.toUpperCase() === ZALO && isEmpty(this.state.searchedZaloTemplates) && this.state.searchingZaloTemplate;
|
|
4705
|
-
const showNoTemplatesFoundOther = ![ZALO].includes(this.state.channel.toUpperCase()) && isEmpty(
|
|
4764
|
+
const showNoTemplatesFoundOther = ![ZALO].includes(this.state.channel.toUpperCase()) && isEmpty(templates) && (useLocalTemplates ? !isLoadingWhenLocal : !this.props.Templates.getAllTemplatesInProgress) && (useLocalTemplates || !isEmpty(this.state.searchText));
|
|
4706
4765
|
const showNoTemplatesFound = showNoTemplatesFoundZalo || showNoTemplatesFoundOther;
|
|
4707
4766
|
|
|
4708
4767
|
return (
|
|
@@ -4746,7 +4805,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4746
4805
|
/>
|
|
4747
4806
|
) : null}
|
|
4748
4807
|
|
|
4749
|
-
{channel.toLowerCase() === RCS_LOWERCASE && !isFullMode ? (
|
|
4808
|
+
{channel.toLowerCase() === RCS_LOWERCASE && !isFullMode && this.state?.hostName !== HOST_INFOBIP ? (
|
|
4750
4809
|
<CapInfoNote
|
|
4751
4810
|
message={formatMessage(messages.rcsOnlyApprovedTemplates)}
|
|
4752
4811
|
/>
|
|
@@ -4759,22 +4818,22 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4759
4818
|
) : null}
|
|
4760
4819
|
<CapRow>
|
|
4761
4820
|
<Pagination
|
|
4762
|
-
templateInProgress={
|
|
4763
|
-
this.props.Templates.getAllTemplatesInProgress
|
|
4764
|
-
}
|
|
4821
|
+
templateInProgress={useLocalTemplates ? isLoadingWhenLocal : this.props.Templates.getAllTemplatesInProgress}
|
|
4765
4822
|
onPageChange={
|
|
4766
|
-
templates.length
|
|
4823
|
+
templates.length
|
|
4824
|
+
? (useLocalTemplates ? (this.props.localTemplatesConfig?.localTemplatesOnPageChange || (() => {})) : this.onPaginationChange)
|
|
4825
|
+
: () => {}
|
|
4767
4826
|
}
|
|
4768
4827
|
>
|
|
4769
4828
|
{this.getTemplateDataForGrid({
|
|
4770
4829
|
previewTemplateId: this.state.zaloPreviewItemId,
|
|
4771
|
-
isLoading,
|
|
4772
|
-
isInitialLoading,
|
|
4830
|
+
isLoading: useLocalTemplates ? isLoadingWhenLocal : isLoading,
|
|
4831
|
+
isInitialLoading: useLocalTemplates ? isLoadingWhenLocal && templates.length === 0 : isInitialLoading,
|
|
4773
4832
|
loadingTip,
|
|
4774
4833
|
channel: this.state.channel,
|
|
4775
4834
|
templates: this.state.searchingZaloTemplate
|
|
4776
4835
|
? this.state.searchedZaloTemplates
|
|
4777
|
-
:
|
|
4836
|
+
: templates,
|
|
4778
4837
|
filterContent,
|
|
4779
4838
|
handlers: {
|
|
4780
4839
|
handlePreviewClick: this.handlePreviewClick,
|
|
@@ -4957,6 +5016,15 @@ Templates.propTypes = {
|
|
|
4957
5016
|
WebPush: PropTypes.object,
|
|
4958
5017
|
smsRegister: PropTypes.any,
|
|
4959
5018
|
isDltFromRcs: PropTypes.bool,
|
|
5019
|
+
isSmsFallbackFromRcs: PropTypes.bool,
|
|
5020
|
+
localTemplatesConfig: PropTypes.shape({
|
|
5021
|
+
useLocalTemplates: PropTypes.bool,
|
|
5022
|
+
localTemplates: PropTypes.arrayOf(PropTypes.object),
|
|
5023
|
+
localTemplatesLoading: PropTypes.bool,
|
|
5024
|
+
localTemplatesLoadingTip: PropTypes.string,
|
|
5025
|
+
localTemplatesFilterContent: PropTypes.node,
|
|
5026
|
+
localTemplatesOnPageChange: PropTypes.func,
|
|
5027
|
+
}),
|
|
4960
5028
|
};
|
|
4961
5029
|
|
|
4962
5030
|
const mapStateToProps = createStructuredSelector({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
call, put, takeLatest, all,
|
|
2
|
+
call, put, takeLatest, takeEvery, all,
|
|
3
3
|
} from 'redux-saga/effects';
|
|
4
4
|
import get from 'lodash/get';
|
|
5
5
|
import { CapNotification } from '@capillarytech/cap-ui-library';
|
|
@@ -8,32 +8,69 @@ import * as Api from '../../services/api';
|
|
|
8
8
|
import * as types from './constants';
|
|
9
9
|
import { saveCdnConfigs, removeAllCdnLocalStorageItems } from '../../utils/cdnTransformation';
|
|
10
10
|
import { COPY_OF } from '../../constants/unified';
|
|
11
|
+
import { fetchSmsTemplatesFromQuery } from './utils/smsTemplatesListApi';
|
|
11
12
|
import { ZALO_TEMPLATE_INFO_REQUEST } from '../Zalo/constants';
|
|
12
13
|
import { getTemplateInfoById } from '../Zalo/saga';
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
export function* getAllTemplates(channel, queryParams) {
|
|
15
|
+
export function* getLocalSmsTemplates(action) {
|
|
16
16
|
try {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
const fetched = yield call(
|
|
18
|
+
fetchSmsTemplatesFromQuery,
|
|
19
|
+
action.queryParams,
|
|
20
|
+
action.intlCopyOf,
|
|
21
|
+
);
|
|
22
|
+
if (typeof action.onSuccess === 'function') {
|
|
23
|
+
yield call(action.onSuccess, fetched);
|
|
24
|
+
}
|
|
25
|
+
} catch (error) {
|
|
26
|
+
if (typeof action.onFailure === 'function') {
|
|
27
|
+
yield call(action.onFailure, error);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function* getAllTemplates(action) {
|
|
33
|
+
try {
|
|
34
|
+
if (action.channel && String(action.channel).toLowerCase() === 'sms') {
|
|
35
|
+
const fetched = yield call(
|
|
36
|
+
fetchSmsTemplatesFromQuery,
|
|
37
|
+
action.queryParams,
|
|
38
|
+
action.intlCopyOf,
|
|
39
|
+
);
|
|
40
|
+
yield put({
|
|
41
|
+
type: types.GET_ALL_TEMPLATES_SUCCESS,
|
|
42
|
+
data: fetched.channelTemplates,
|
|
43
|
+
weCRMTemplate: fetched.weCRMTemplate,
|
|
44
|
+
isReset: get(action, 'queryParams.page') === 1,
|
|
45
|
+
});
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const result = yield call(Api.getAllTemplates, action);
|
|
50
|
+
const channelTemplates = (action.channel === 'wechat')
|
|
51
|
+
? { templates: [...result.response.mapped, ...result.response.richmedia] }
|
|
52
|
+
: result.response;
|
|
53
|
+
if (action.channel === 'wechat' && action.queryParams && action.queryParams.sortBy && action.queryParams.sortBy.toLocaleLowerCase() === ("Most Recent").toLocaleLowerCase()) {
|
|
21
54
|
channelTemplates.templates.sort((a, b) => {
|
|
22
55
|
const dateA = new Date(a.updatedAt);
|
|
23
56
|
const dateB = new Date(b.updatedAt);
|
|
24
57
|
return dateB - dateA;
|
|
25
58
|
});
|
|
26
|
-
} else if (
|
|
59
|
+
} else if (action.channel === 'wechat' && action.queryParams && action.queryParams.sortBy && action.queryParams.sortBy.toLocaleLowerCase() === ("Alphabetically").toLocaleLowerCase()) {
|
|
27
60
|
channelTemplates.templates.sort((a, b) => b.name - a.name);
|
|
28
61
|
}
|
|
29
|
-
|
|
30
|
-
if (channel.intlCopyOf && channelTemplates?.templates) {
|
|
62
|
+
if (action.intlCopyOf && channelTemplates?.templates) {
|
|
31
63
|
channelTemplates.templates = channelTemplates.templates.map((template) => ({
|
|
32
64
|
...template,
|
|
33
|
-
name: template.name.replace(new RegExp(COPY_OF, 'g'),
|
|
65
|
+
name: template.name.replace(new RegExp(COPY_OF, 'g'), action.intlCopyOf),
|
|
34
66
|
}));
|
|
35
67
|
}
|
|
36
|
-
yield put({
|
|
68
|
+
yield put({
|
|
69
|
+
type: types.GET_ALL_TEMPLATES_SUCCESS,
|
|
70
|
+
data: channelTemplates,
|
|
71
|
+
weCRMTemplate: result.response.unMapped,
|
|
72
|
+
isReset: get(action, 'queryParams.page') === 1,
|
|
73
|
+
});
|
|
37
74
|
} catch (error) {
|
|
38
75
|
yield put({ type: types.GET_ALL_TEMPLATES_FAILURE, error });
|
|
39
76
|
}
|
|
@@ -265,6 +302,11 @@ export function* watchGetAllTemplates() {
|
|
|
265
302
|
yield takeLatest(types.GET_ALL_TEMPLATES_REQUEST, getAllTemplates);
|
|
266
303
|
}
|
|
267
304
|
|
|
305
|
+
|
|
306
|
+
export function* watchGetLocalSmsTemplates() {
|
|
307
|
+
yield takeEvery(types.GET_LOCAL_SMS_TEMPLATES_REQUEST, getLocalSmsTemplates);
|
|
308
|
+
}
|
|
309
|
+
|
|
268
310
|
export function* watchDeleteTemplate() {
|
|
269
311
|
yield takeLatest(types.DELETE_TEMPLATE_REQUEST, deleteTemplate);
|
|
270
312
|
}
|
|
@@ -318,6 +360,7 @@ export function* watchForGetTemplateInfoById() {
|
|
|
318
360
|
// All sagas to be loaded
|
|
319
361
|
export default [
|
|
320
362
|
watchGetAllTemplates,
|
|
363
|
+
watchGetLocalSmsTemplates,
|
|
321
364
|
watchDeleteTemplate,
|
|
322
365
|
watchDeleteRcsTemplate,
|
|
323
366
|
watchGetUserList,
|
|
@@ -334,6 +377,7 @@ export default [
|
|
|
334
377
|
export function* v2TemplateSaga() {
|
|
335
378
|
yield all([
|
|
336
379
|
watchGetAllTemplates(),
|
|
380
|
+
watchGetLocalSmsTemplates(),
|
|
337
381
|
watchDeleteTemplate(),
|
|
338
382
|
watchDeleteRcsTemplate(),
|
|
339
383
|
watchGetUserList(),
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
6
|
+
import '@testing-library/jest-dom';
|
|
7
|
+
import TemplatesActionBar from '../TemplatesActionBar';
|
|
8
|
+
|
|
9
|
+
jest.mock('@capillarytech/cap-ui-library/CapInput', () => {
|
|
10
|
+
const React = require('react');
|
|
11
|
+
function Search(props) {
|
|
12
|
+
return React.createElement('input', {
|
|
13
|
+
'data-testid': 'cap-input-search',
|
|
14
|
+
value: props?.value,
|
|
15
|
+
placeholder: props?.placeholder,
|
|
16
|
+
onChange: props?.onChange,
|
|
17
|
+
onKeyDown: (e) => {
|
|
18
|
+
if (e.key === 'Enter' && props.onPressEnter) {
|
|
19
|
+
props.onPressEnter(e);
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function CapInput() {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
CapInput.Search = Search;
|
|
28
|
+
return { __esModule: true, default: CapInput };
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
jest.mock('@capillarytech/cap-ui-library/CapButton', () => {
|
|
32
|
+
const React = require('react');
|
|
33
|
+
return function CapButton(props) {
|
|
34
|
+
return React.createElement('button', {
|
|
35
|
+
type: 'button',
|
|
36
|
+
'data-testid': 'cta',
|
|
37
|
+
onClick: props?.onClick,
|
|
38
|
+
disabled: props?.disabled,
|
|
39
|
+
}, props?.children);
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('TemplatesActionBar', () => {
|
|
44
|
+
it('renders search when searchPlaceholder is set', () => {
|
|
45
|
+
render(
|
|
46
|
+
<TemplatesActionBar
|
|
47
|
+
searchPlaceholder="Find templates"
|
|
48
|
+
searchValue="hi"
|
|
49
|
+
ctaLabel="Create"
|
|
50
|
+
/>,
|
|
51
|
+
);
|
|
52
|
+
expect(screen.getByTestId('cap-input-search')).toHaveAttribute('placeholder', 'Find templates');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('omits search when searchPlaceholder is empty', () => {
|
|
56
|
+
const { container } = render(
|
|
57
|
+
<TemplatesActionBar searchPlaceholder="" ctaLabel="Go" />,
|
|
58
|
+
);
|
|
59
|
+
expect(container.querySelector('[data-testid="cap-input-search"]')).toBeNull();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('fires onSearchChange and onCtaClick', () => {
|
|
63
|
+
const onSearchChange = jest.fn();
|
|
64
|
+
const onCtaClick = jest.fn();
|
|
65
|
+
render(
|
|
66
|
+
<TemplatesActionBar
|
|
67
|
+
searchPlaceholder="S"
|
|
68
|
+
onSearchChange={onSearchChange}
|
|
69
|
+
ctaLabel="New"
|
|
70
|
+
onCtaClick={onCtaClick}
|
|
71
|
+
/>,
|
|
72
|
+
);
|
|
73
|
+
fireEvent.change(screen.getByTestId('cap-input-search'), { target: { value: 'x' } });
|
|
74
|
+
fireEvent.click(screen.getByTestId('cta'));
|
|
75
|
+
expect(onSearchChange).toHaveBeenCalled();
|
|
76
|
+
expect(onCtaClick).toHaveBeenCalled();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('fires onSearch when Enter is pressed (antd Input has no native onSearch)', () => {
|
|
80
|
+
const onSearch = jest.fn();
|
|
81
|
+
render(
|
|
82
|
+
<TemplatesActionBar
|
|
83
|
+
searchPlaceholder="S"
|
|
84
|
+
searchValue="query"
|
|
85
|
+
onSearch={onSearch}
|
|
86
|
+
ctaLabel="New"
|
|
87
|
+
/>,
|
|
88
|
+
);
|
|
89
|
+
const input = screen.getByTestId('cap-input-search');
|
|
90
|
+
fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' });
|
|
91
|
+
expect(onSearch).toHaveBeenCalledWith('query');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('renders ctaNode instead of default button when provided', () => {
|
|
95
|
+
render(
|
|
96
|
+
<TemplatesActionBar
|
|
97
|
+
searchPlaceholder="S"
|
|
98
|
+
ctaNode={<span data-testid="custom-cta">Custom</span>}
|
|
99
|
+
/>,
|
|
100
|
+
);
|
|
101
|
+
expect(screen.getByTestId('custom-cta')).toBeInTheDocument();
|
|
102
|
+
expect(screen.queryByTestId('cta')).toBeNull();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('hides CTA area when showCta is false', () => {
|
|
106
|
+
const { container } = render(
|
|
107
|
+
<TemplatesActionBar searchPlaceholder="S" showCta={false} ctaLabel="X" />,
|
|
108
|
+
);
|
|
109
|
+
expect(container.querySelector('[data-testid="cta"]')).toBeNull();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('renders children in toolbar row', () => {
|
|
113
|
+
render(
|
|
114
|
+
<TemplatesActionBar searchPlaceholder="S" ctaLabel="C">
|
|
115
|
+
<span data-testid="child">extra</span>
|
|
116
|
+
</TemplatesActionBar>,
|
|
117
|
+
);
|
|
118
|
+
expect(screen.getByTestId('child')).toBeInTheDocument();
|
|
119
|
+
});
|
|
120
|
+
});
|