@capillarytech/creatives-library 8.0.358 → 8.0.359-alpha.1
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/CapImageUpload/index.js +2 -2
- 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 +214 -21
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +83 -9
- 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 +150 -4
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
- package/v2Components/CommonTestAndPreview/constants.js +38 -2
- package/v2Components/CommonTestAndPreview/index.js +810 -222
- 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 +133 -4
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +31 -24
- package/v2Components/FormBuilder/index.js +5 -4
- 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 +37 -22
- 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 +17 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +36 -4
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +14 -5
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +36 -5
- package/v2Containers/CreativesContainer/constants.js +9 -0
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +79 -0
- package/v2Containers/CreativesContainer/index.js +322 -103
- package/v2Containers/CreativesContainer/index.scss +83 -1
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +79 -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/MobilePush/Create/test/saga.test.js +2 -2
- package/v2Containers/Rcs/constants.js +120 -11
- package/v2Containers/Rcs/index.js +2577 -812
- package/v2Containers/Rcs/index.scss +281 -8
- package/v2Containers/Rcs/messages.js +34 -3
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +225 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +98036 -70145
- 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 +121 -53
- 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
|
@@ -125,7 +125,7 @@ import { INAPP_LAYOUT_DETAILS, INAPP_MESSAGE_LAYOUT_TYPES } from '../InApp/const
|
|
|
125
125
|
import { ZALO_STATUS_OPTIONS, ZALO_STATUSES } from '../Zalo/constants';
|
|
126
126
|
import { getWhatsappContent, getWhatsappStatus, getWhatsappCategory, getWhatsappCta, getWhatsappQuickReply, getWhatsappAutoFill, getWhatsappCarouselButtonView } from '../Whatsapp/utils';
|
|
127
127
|
import { getRCSContent } from '../Rcs/utils';
|
|
128
|
-
import {RCS_STATUSES} from '../Rcs/constants';
|
|
128
|
+
import { RCS_STATUSES, HOST_INFOBIP } from '../Rcs/constants';
|
|
129
129
|
import zaloMessages from '../Zalo/messages';
|
|
130
130
|
import rcsMessages from '../Rcs/messages';
|
|
131
131
|
import inAppMessages from '../InApp/messages';
|
|
@@ -463,7 +463,13 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
463
463
|
if (this.props.location.query.type === 'embedded') {
|
|
464
464
|
this.props.actions.resetAccount();
|
|
465
465
|
}
|
|
466
|
-
|
|
466
|
+
// When using local templates (e.g. SMS fallback selector), do not fetch from API or we overwrite global store and break background RCS list
|
|
467
|
+
const useLocalTemplates = get(
|
|
468
|
+
this.props,
|
|
469
|
+
'localTemplatesConfig.useLocalTemplates',
|
|
470
|
+
get(this.props, 'useLocalTemplates', false),
|
|
471
|
+
);
|
|
472
|
+
if (!useLocalTemplates && [LINE_LOWERCASE, VIBER_CHANNEL, FACEBOOK_CHANNEL, SMS_LOWERCASE, EMAIL_LOWERCASE, EBILL_LOWERCASE].includes((this.state.channel || '').toLowerCase())) {
|
|
467
473
|
const queryParams = {
|
|
468
474
|
// name: this.state.searchText,
|
|
469
475
|
// sortBy: this.state.sortBy,
|
|
@@ -546,7 +552,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
546
552
|
selectedTemplateForPreview: null,
|
|
547
553
|
testAndPreviewContent: null,
|
|
548
554
|
}, () => {
|
|
549
|
-
if ([
|
|
555
|
+
if ([LINE_LOWERCASE, VIBER_CHANNEL, FACEBOOK_CHANNEL, SMS_LOWERCASE, EMAIL_LOWERCASE, EBILL_LOWERCASE, RCS_LOWERCASE].includes(this.state.channel.toLowerCase()) || (this.state.channel.toLowerCase() === 'wechat' && !isEmpty(nextProps.Templates.selectedWeChatAccount))) {
|
|
550
556
|
if (this.state.channel.toLowerCase() === 'wechat' && !isEmpty(nextProps.Templates.selectedWeChatAccount)) {
|
|
551
557
|
params.wecrmId = (nextProps.Templates.selectedWeChatAccount.configs || {}).wecrm_app_id;
|
|
552
558
|
params.wecrmToken = (nextProps.Templates.selectedWeChatAccount.configs || {}).wecrm_token;
|
|
@@ -1009,8 +1015,16 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
1009
1015
|
|
|
1010
1016
|
componentWillUnmount() {
|
|
1011
1017
|
window.removeEventListener("message", this.handleFrameTasks);
|
|
1012
|
-
|
|
1013
|
-
|
|
1018
|
+
// When using local templates (e.g. SMS fallback selector), do not clear global store or background RCS list is wiped
|
|
1019
|
+
const useLocalTemplates = get(
|
|
1020
|
+
this.props,
|
|
1021
|
+
'localTemplatesConfig.useLocalTemplates',
|
|
1022
|
+
get(this.props, 'useLocalTemplates', false),
|
|
1023
|
+
);
|
|
1024
|
+
if (!useLocalTemplates) {
|
|
1025
|
+
this.props.actions.resetTemplateStoreData();
|
|
1026
|
+
this.props.globalActions.clearMetaEntities();
|
|
1027
|
+
}
|
|
1014
1028
|
// Clear any pending timeouts to prevent memory leaks
|
|
1015
1029
|
if (this._clearEditTimeout) {
|
|
1016
1030
|
clearTimeout(this._clearEditTimeout);
|
|
@@ -1867,12 +1881,20 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
1867
1881
|
}
|
|
1868
1882
|
|
|
1869
1883
|
filterRcsTemplates = (templates) => {
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1884
|
+
const selectedRcsAccountName = this.props?.Templates?.selectedRcsAccount?.name || '';
|
|
1885
|
+
const hostName = this.state?.hostName;
|
|
1886
|
+
let nextTemplates = templates || [];
|
|
1887
|
+
if (selectedRcsAccountName) {
|
|
1888
|
+
nextTemplates = nextTemplates.filter(
|
|
1889
|
+
(t) => get(t, 'versions.base.content.RCS.rcsContent.accountName', '') === selectedRcsAccountName
|
|
1890
|
+
);
|
|
1874
1891
|
}
|
|
1875
|
-
|
|
1892
|
+
if (!this.props.isFullMode && hostName !== HOST_INFOBIP) {
|
|
1893
|
+
return nextTemplates.filter(
|
|
1894
|
+
(t) => get(t, 'versions.base.content.RCS.rcsContent.cardContent[0].Status', 'unavailable') === RCS_STATUSES.approved
|
|
1895
|
+
);
|
|
1896
|
+
}
|
|
1897
|
+
return nextTemplates;
|
|
1876
1898
|
}
|
|
1877
1899
|
|
|
1878
1900
|
filterZaloTemplates = (templates) => {
|
|
@@ -2062,7 +2084,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2062
2084
|
style={{ marginRight: "16px" }}
|
|
2063
2085
|
type="eye"
|
|
2064
2086
|
onClick={() => {
|
|
2065
|
-
if (!this.props.isFullMode || this.props.isDltFromRcs) {
|
|
2087
|
+
if (!this.props.isFullMode || this.props.isDltFromRcs || this.props.isSmsFallbackFromRcs) {
|
|
2066
2088
|
if (!get(template, "versions.base.content.zalo.previewUrl", "")) {
|
|
2067
2089
|
this.setState({ zaloPreviewItemId: template?._id });
|
|
2068
2090
|
}
|
|
@@ -2544,13 +2566,14 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2544
2566
|
<CapRow type="flex" align="middle">
|
|
2545
2567
|
{isCardArchiveEligible && this.renderCardSelectionCheckbox({ templateId: template._id, selectedIds: selectedIdsArrayForCard, isDisabled: isAnyArchiveInProgress })}
|
|
2546
2568
|
<CapLabel className="whatsapp-rcs-template-name">{name}</CapLabel>
|
|
2547
|
-
<CapRow type="flex" align="middle" className="rcs-status-container zalo-status-color">
|
|
2569
|
+
{this.state.hostName !== HOST_INFOBIP && <CapRow type="flex" align="middle" className="rcs-status-container zalo-status-color">
|
|
2548
2570
|
<CapStatus
|
|
2549
2571
|
type={statusDisplay}
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2572
|
+
text={statusDisplay && this.props.intl.formatMessage(rcsMessages?.[`${statusDisplay}_STATUS`])}
|
|
2573
|
+
labelType="label3"
|
|
2574
|
+
/>
|
|
2575
|
+
</CapRow>
|
|
2576
|
+
}
|
|
2554
2577
|
</CapRow>
|
|
2555
2578
|
);
|
|
2556
2579
|
|
|
@@ -3126,6 +3149,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
3126
3149
|
let routeParams = {};
|
|
3127
3150
|
const {fbAdManager} = this.props;
|
|
3128
3151
|
const isLibraryMode = this.isEnabledInLibraryModule("callCreateFromProps");
|
|
3152
|
+
|
|
3129
3153
|
if (!isLibraryMode) {
|
|
3130
3154
|
timeTracker.startTimer(CHANNEL_CREATE_TRACK_MAPPING[channel]);
|
|
3131
3155
|
}
|
|
@@ -3475,6 +3499,13 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
3475
3499
|
this.setState({modeType});
|
|
3476
3500
|
}
|
|
3477
3501
|
const { _id: id } = template;
|
|
3502
|
+
const {
|
|
3503
|
+
localTemplatesConfig,
|
|
3504
|
+
fbAdManager,
|
|
3505
|
+
isDltFromRcs,
|
|
3506
|
+
isSmsFallbackFromRcs,
|
|
3507
|
+
onSelectTemplate,
|
|
3508
|
+
} = this.props;
|
|
3478
3509
|
const type = this.props.location.query.type;
|
|
3479
3510
|
const module = this.props.location.query.module;
|
|
3480
3511
|
const isLanguageSupport = (this.props.location.query.isLanguageSupport) ? this.props.location.query.isLanguageSupport : false;
|
|
@@ -3560,10 +3591,12 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
3560
3591
|
}
|
|
3561
3592
|
if (this.isEnabledInLibraryModule("callSelectFromProps")) {
|
|
3562
3593
|
let data = id;
|
|
3563
|
-
if (
|
|
3594
|
+
if (localTemplatesConfig?.useLocalTemplates) {
|
|
3595
|
+
data = template;
|
|
3596
|
+
} else if (fbAdManager || isDltFromRcs || isSmsFallbackFromRcs) {
|
|
3564
3597
|
data = this.selectTemplate(id);
|
|
3565
3598
|
}
|
|
3566
|
-
|
|
3599
|
+
onSelectTemplate(data, fbAdManager);
|
|
3567
3600
|
} else {
|
|
3568
3601
|
timeTracker.startTimer(CHANNEL_EDIT_TRACK_MAPPING[this.state.channel.toLowerCase()]);
|
|
3569
3602
|
if (this.state.channel.toLowerCase() === 'ebill') {
|
|
@@ -4522,9 +4555,14 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4522
4555
|
const isWechatEmbedded = !this.props.isFullMode && channel.toUpperCase() === WECHAT;
|
|
4523
4556
|
const channelLowerCase = (channel || '').toLowerCase();
|
|
4524
4557
|
const isTraiDltFeature = this.checkDLTfeatureEnable();
|
|
4525
|
-
|
|
4526
4558
|
const createButton =
|
|
4527
|
-
(
|
|
4559
|
+
(
|
|
4560
|
+
(
|
|
4561
|
+
channelLowerCase === WHATSAPP_LOWERCASE
|
|
4562
|
+
|| channelLowerCase === RCS_LOWERCASE
|
|
4563
|
+
)
|
|
4564
|
+
&& !this.props.isFullMode
|
|
4565
|
+
)
|
|
4528
4566
|
? (
|
|
4529
4567
|
<CapLink
|
|
4530
4568
|
onClick={this.openCreativesFullMode}
|
|
@@ -4556,17 +4594,22 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4556
4594
|
const _renderSelectedIdsArray = _renderSelectedIds && typeof _renderSelectedIds.toJS === 'function' ? _renderSelectedIds.toJS() : (Array.isArray(_renderSelectedIds) ? _renderSelectedIds : []);
|
|
4557
4595
|
const _renderHasSelection = _isArchivalEnabled && this.props.isFullMode && _renderSelectedIdsArray.length > 0;
|
|
4558
4596
|
|
|
4559
|
-
const
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4597
|
+
const useLocalTemplates = this.props.localTemplatesConfig?.useLocalTemplates;
|
|
4598
|
+
const builtFilterContent = ((isfilterContentVisisble || [WECHAT, MOBILE_PUSH, INAPP].includes(this.state.channel.toUpperCase())) && (
|
|
4599
|
+
<div className="action-container">
|
|
4600
|
+
<div className="action-container__toolbar-row">
|
|
4601
|
+
{isfilterContentVisisble ? (
|
|
4602
|
+
<CapInput.Search
|
|
4603
|
+
className="search-text"
|
|
4604
|
+
placeholder={this.props.intl.formatMessage(messages.searchText)}
|
|
4605
|
+
value={this.state.searchText}
|
|
4606
|
+
onChange={(e) => this.searchTemplate(e.target.value, this.state.channel)}
|
|
4607
|
+
onSearch={() => this.searchTemplate(this.state.searchText, this.state.channel)}
|
|
4608
|
+
onClear={() => this.searchTemplate('', this.state.channel)}
|
|
4609
|
+
onScroll={(e) => e.stopPropagation()}
|
|
4610
|
+
disabled={this.checkSearchDisabled()}
|
|
4611
|
+
/>
|
|
4612
|
+
) : null}
|
|
4570
4613
|
{
|
|
4571
4614
|
channel.toUpperCase() === WECHAT && <CapRadio.CapRadioGroup className="wechat-filters" defaultValue={wechatFilter} onChange={this.setWechatFilter}>
|
|
4572
4615
|
<CapRadio.Button value={WECHAT_FILTERS.ALL}><CapLabel type="label2">
|
|
@@ -4713,16 +4756,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4713
4756
|
)
|
|
4714
4757
|
}
|
|
4715
4758
|
<div className="template-listing-header-actions">
|
|
4716
|
-
{!_isArchivedMode && !_renderHasSelection && (
|
|
4717
|
-
this.state?.channel?.toLowerCase() === WHATSAPP_LOWERCASE && (isWhatsappCountExeeded) ? (
|
|
4718
|
-
<CapTooltip title={whatsappCountExceedText}>
|
|
4719
|
-
<div className="button-disabled-tooltip-wrapper">
|
|
4720
|
-
{createButton}
|
|
4721
|
-
</div>
|
|
4722
|
-
</CapTooltip>
|
|
4723
|
-
)
|
|
4724
|
-
: isfilterContentVisisble && !isWechatEmbedded && !this.props.isDltFromRcs && createButton
|
|
4725
|
-
)}
|
|
4726
4759
|
{/* More (⋯) menu: full mode only, not archived mode, not Zalo (no archive support), not when selection active, archive flag enabled */}
|
|
4727
4760
|
{commonUtil.hasCreativesArchivalEnabled() && !_isArchivedMode && !_renderHasSelection && this.props.isFullMode && this.props.location.query.type !== EMBEDDED && channelLowerCase !== ZALO_LOWERCASE && (
|
|
4728
4761
|
<CapDropdown
|
|
@@ -4745,7 +4778,28 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4745
4778
|
</CapDropdown>
|
|
4746
4779
|
)}
|
|
4747
4780
|
</div>
|
|
4748
|
-
</div>
|
|
4781
|
+
</div>
|
|
4782
|
+
<div>
|
|
4783
|
+
<div className="action-container__create-row">
|
|
4784
|
+
{
|
|
4785
|
+
isfilterContentVisisble && !isWechatEmbedded && !this.props.isDltFromRcs && !this.props.isSmsFallbackFromRcs && (
|
|
4786
|
+
this.state?.channel?.toLowerCase() === WHATSAPP_LOWERCASE && isWhatsappCountExeeded ? (
|
|
4787
|
+
<CapTooltip title={whatsappCountExceedText}>
|
|
4788
|
+
<div className="button-disabled-tooltip-wrapper">
|
|
4789
|
+
{createButton}
|
|
4790
|
+
</div>
|
|
4791
|
+
</CapTooltip>
|
|
4792
|
+
) : createButton
|
|
4793
|
+
)
|
|
4794
|
+
}
|
|
4795
|
+
</div>
|
|
4796
|
+
</div>
|
|
4797
|
+
</div>
|
|
4798
|
+
));
|
|
4799
|
+
const localTemplatesFilterContent = get(this.props, 'localTemplatesConfig.localTemplatesFilterContent', null);
|
|
4800
|
+
const filterContent = (useLocalTemplates && localTemplatesFilterContent) != null
|
|
4801
|
+
? localTemplatesFilterContent
|
|
4802
|
+
: builtFilterContent;
|
|
4749
4803
|
let htmlPreviewContent = "";
|
|
4750
4804
|
if (this.state.channel.toLowerCase() === 'ebill') {
|
|
4751
4805
|
htmlPreviewContent = this.state.previewTemplate && this.state.previewTemplate.versions && this.state.previewTemplate.versions.base && this.state.previewTemplate.versions.base['ebill-editor'];
|
|
@@ -4755,7 +4809,10 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4755
4809
|
|
|
4756
4810
|
|
|
4757
4811
|
const creativesParams = this.getCreativesParams();
|
|
4758
|
-
const templates =
|
|
4812
|
+
const templates = useLocalTemplates
|
|
4813
|
+
? (this.props.localTemplatesConfig?.localTemplates || [])
|
|
4814
|
+
: (this.props.TemplatesList || []);
|
|
4815
|
+
const isLoadingWhenLocal = useLocalTemplates && !!this.props.localTemplatesConfig?.localTemplatesLoading;
|
|
4759
4816
|
const {route} = this.props;
|
|
4760
4817
|
const loadingTipMap = {
|
|
4761
4818
|
sendingFile: 'uploadingFile',
|
|
@@ -4770,9 +4827,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4770
4827
|
(deleteRcsTemplateInProgress && 'deletingTemplate') ||
|
|
4771
4828
|
(this.props.EmailCreate.duplicateTemplateInProgress && 'duplicatingTemplate');
|
|
4772
4829
|
|
|
4773
|
-
const loadingTip =
|
|
4830
|
+
const loadingTip = useLocalTemplates && this.props.localTemplatesConfig?.localTemplatesLoadingTip
|
|
4831
|
+
? this.props.localTemplatesConfig.localTemplatesLoadingTip
|
|
4832
|
+
: (messages[loadingTipIntl] ? this.props.intl.formatMessage(messages[loadingTipIntl]) : this.props.intl.formatMessage(messages.gettingAllTemplates));
|
|
4774
4833
|
const showNoTemplatesFoundZalo = this.state.channel.toUpperCase() === ZALO && isEmpty(this.state.searchedZaloTemplates) && this.state.searchingZaloTemplate;
|
|
4775
|
-
const showNoTemplatesFoundOther = ![ZALO].includes(this.state.channel.toUpperCase()) && isEmpty(
|
|
4834
|
+
const showNoTemplatesFoundOther = ![ZALO].includes(this.state.channel.toUpperCase()) && isEmpty(templates) && (useLocalTemplates ? !isLoadingWhenLocal : !this.props.Templates.getAllTemplatesInProgress) && (useLocalTemplates || !isEmpty(this.state.searchText));
|
|
4776
4835
|
const showNoTemplatesFound = showNoTemplatesFoundZalo || showNoTemplatesFoundOther;
|
|
4777
4836
|
|
|
4778
4837
|
return (
|
|
@@ -4816,7 +4875,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4816
4875
|
/>
|
|
4817
4876
|
) : null}
|
|
4818
4877
|
|
|
4819
|
-
{channel.toLowerCase() === RCS_LOWERCASE && !isFullMode ? (
|
|
4878
|
+
{channel.toLowerCase() === RCS_LOWERCASE && !isFullMode && this.state?.hostName !== HOST_INFOBIP ? (
|
|
4820
4879
|
<CapInfoNote
|
|
4821
4880
|
message={formatMessage(messages.rcsOnlyApprovedTemplates)}
|
|
4822
4881
|
/>
|
|
@@ -4829,22 +4888,22 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4829
4888
|
) : null}
|
|
4830
4889
|
<CapRow>
|
|
4831
4890
|
<Pagination
|
|
4832
|
-
templateInProgress={
|
|
4833
|
-
this.props.Templates.getAllTemplatesInProgress
|
|
4834
|
-
}
|
|
4891
|
+
templateInProgress={useLocalTemplates ? isLoadingWhenLocal : this.props.Templates.getAllTemplatesInProgress}
|
|
4835
4892
|
onPageChange={
|
|
4836
|
-
templates.length
|
|
4893
|
+
templates.length
|
|
4894
|
+
? (useLocalTemplates ? (this.props.localTemplatesConfig?.localTemplatesOnPageChange || (() => {})) : this.onPaginationChange)
|
|
4895
|
+
: () => {}
|
|
4837
4896
|
}
|
|
4838
4897
|
>
|
|
4839
4898
|
{this.getTemplateDataForGrid({
|
|
4840
4899
|
previewTemplateId: this.state.zaloPreviewItemId,
|
|
4841
|
-
isLoading,
|
|
4842
|
-
isInitialLoading,
|
|
4900
|
+
isLoading: useLocalTemplates ? isLoadingWhenLocal : isLoading,
|
|
4901
|
+
isInitialLoading: useLocalTemplates ? isLoadingWhenLocal && templates.length === 0 : isInitialLoading,
|
|
4843
4902
|
loadingTip,
|
|
4844
4903
|
channel: this.state.channel,
|
|
4845
4904
|
templates: this.state.searchingZaloTemplate
|
|
4846
4905
|
? this.state.searchedZaloTemplates
|
|
4847
|
-
:
|
|
4906
|
+
: templates,
|
|
4848
4907
|
filterContent,
|
|
4849
4908
|
handlers: {
|
|
4850
4909
|
handlePreviewClick: this.handlePreviewClick,
|
|
@@ -5027,6 +5086,15 @@ Templates.propTypes = {
|
|
|
5027
5086
|
WebPush: PropTypes.object,
|
|
5028
5087
|
smsRegister: PropTypes.any,
|
|
5029
5088
|
isDltFromRcs: PropTypes.bool,
|
|
5089
|
+
isSmsFallbackFromRcs: PropTypes.bool,
|
|
5090
|
+
localTemplatesConfig: PropTypes.shape({
|
|
5091
|
+
useLocalTemplates: PropTypes.bool,
|
|
5092
|
+
localTemplates: PropTypes.arrayOf(PropTypes.object),
|
|
5093
|
+
localTemplatesLoading: PropTypes.bool,
|
|
5094
|
+
localTemplatesLoadingTip: PropTypes.string,
|
|
5095
|
+
localTemplatesFilterContent: PropTypes.node,
|
|
5096
|
+
localTemplatesOnPageChange: PropTypes.func,
|
|
5097
|
+
}),
|
|
5030
5098
|
};
|
|
5031
5099
|
|
|
5032
5100
|
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, initCdnConfigFromEnv } 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
|
}
|
|
@@ -270,6 +307,11 @@ export function* watchGetAllTemplates() {
|
|
|
270
307
|
yield takeLatest(types.GET_ALL_TEMPLATES_REQUEST, getAllTemplates);
|
|
271
308
|
}
|
|
272
309
|
|
|
310
|
+
|
|
311
|
+
export function* watchGetLocalSmsTemplates() {
|
|
312
|
+
yield takeEvery(types.GET_LOCAL_SMS_TEMPLATES_REQUEST, getLocalSmsTemplates);
|
|
313
|
+
}
|
|
314
|
+
|
|
273
315
|
export function* watchDeleteTemplate() {
|
|
274
316
|
yield takeLatest(types.DELETE_TEMPLATE_REQUEST, deleteTemplate);
|
|
275
317
|
}
|
|
@@ -323,6 +365,7 @@ export function* watchForGetTemplateInfoById() {
|
|
|
323
365
|
// All sagas to be loaded
|
|
324
366
|
export default [
|
|
325
367
|
watchGetAllTemplates,
|
|
368
|
+
watchGetLocalSmsTemplates,
|
|
326
369
|
watchDeleteTemplate,
|
|
327
370
|
watchDeleteRcsTemplate,
|
|
328
371
|
watchGetUserList,
|
|
@@ -339,6 +382,7 @@ export default [
|
|
|
339
382
|
export function* v2TemplateSaga() {
|
|
340
383
|
yield all([
|
|
341
384
|
watchGetAllTemplates(),
|
|
385
|
+
watchGetLocalSmsTemplates(),
|
|
342
386
|
watchDeleteTemplate(),
|
|
343
387
|
watchDeleteRcsTemplate(),
|
|
344
388
|
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
|
+
});
|