@capillarytech/creatives-library 8.0.345-alpha.14 → 8.0.345-alpha.15
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 +13 -0
- package/utils/commonUtils.js +19 -1
- package/utils/rcsPayloadUtils.js +92 -0
- package/utils/templateVarUtils.js +201 -0
- package/utils/tests/templateVarUtils.test.js +204 -0
- package/v2Components/CapActionButton/constants.js +7 -0
- package/v2Components/CapActionButton/index.js +167 -109
- 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 +70 -49
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +207 -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 +160 -15
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js.rej +18 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +341 -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 +676 -186
- package/v2Components/CommonTestAndPreview/messages.js +49 -3
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
- package/v2Components/CommonTestAndPreview/sagas.js +15 -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 +2 -2
- package/v2Components/FormBuilder/index.js +8 -10
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +87 -0
- package/v2Components/SmsFallback/constants.js +73 -0
- package/v2Components/SmsFallback/index.js +955 -0
- package/v2Components/SmsFallback/index.scss +265 -0
- package/v2Components/SmsFallback/messages.js +78 -0
- package/v2Components/SmsFallback/smsFallbackUtils.js +118 -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 +197 -0
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +277 -0
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
- package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
- package/v2Components/TemplatePreview/_templatePreview.scss +33 -23
- package/v2Components/TemplatePreview/constants.js +2 -0
- package/v2Components/TemplatePreview/index.js +143 -28
- 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 +67 -0
- package/v2Containers/CreativesContainer/index.js +300 -103
- 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/reducer.js +3 -11
- package/v2Containers/Email/sagas.js +5 -9
- package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +0 -4
- package/v2Containers/Email/tests/sagas.test.js +3 -21
- package/v2Containers/Rcs/constants.js +119 -8
- package/v2Containers/Rcs/index.js +2379 -807
- package/v2Containers/Rcs/index.js.rej +1336 -0
- package/v2Containers/Rcs/index.scss +276 -6
- package/v2Containers/Rcs/index.scss.rej +74 -0
- package/v2Containers/Rcs/messages.js +38 -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/__snapshots__/utils.test.js.snap.rej +128 -0
- 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 +100 -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 +636 -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 +163 -2
- package/v2Containers/Templates/actions.js +11 -0
- package/v2Containers/Templates/constants.js +2 -0
- package/v2Containers/Templates/index.js +119 -54
- package/v2Containers/Templates/sagas.js +57 -12
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1043 -1079
- package/v2Containers/Templates/tests/sagas.test.js +193 -123
- 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/Whatsapp/index.js +3 -20
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
|
@@ -132,7 +132,7 @@ import { INAPP_LAYOUT_DETAILS, INAPP_MESSAGE_LAYOUT_TYPES, INAPP_MEDIA_TYPES, BI
|
|
|
132
132
|
import { ZALO_STATUS_OPTIONS, ZALO_STATUSES } from '../Zalo/constants';
|
|
133
133
|
import { getWhatsappContent, getWhatsappStatus, getWhatsappCategory, getWhatsappCta, getWhatsappQuickReply, getWhatsappAutoFill, getWhatsappCarouselButtonView } from '../Whatsapp/utils';
|
|
134
134
|
import { getRCSContent } from '../Rcs/utils';
|
|
135
|
-
import {RCS_STATUSES} from '../Rcs/constants';
|
|
135
|
+
import { RCS_STATUSES, HOST_INFOBIP } from '../Rcs/constants';
|
|
136
136
|
import zaloMessages from '../Zalo/messages';
|
|
137
137
|
import rcsMessages from '../Rcs/messages';
|
|
138
138
|
import inAppMessages from '../InApp/messages';
|
|
@@ -468,7 +468,13 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
468
468
|
if (this.props.location.query.type === 'embedded') {
|
|
469
469
|
this.props.actions.resetAccount();
|
|
470
470
|
}
|
|
471
|
-
|
|
471
|
+
// When using local templates (e.g. SMS fallback selector), do not fetch from API or we overwrite global store and break background RCS list
|
|
472
|
+
const useLocalTemplates = get(
|
|
473
|
+
this.props,
|
|
474
|
+
'localTemplatesConfig.useLocalTemplates',
|
|
475
|
+
get(this.props, 'useLocalTemplates', false),
|
|
476
|
+
);
|
|
477
|
+
if (!useLocalTemplates && ['line', VIBER_CHANNEL, FACEBOOK_CHANNEL, 'sms', 'email', 'ebill'].includes((this.state.channel || '').toLowerCase())) {
|
|
472
478
|
const queryParams = {
|
|
473
479
|
// name: this.state.searchText,
|
|
474
480
|
// sortBy: this.state.sortBy,
|
|
@@ -981,8 +987,16 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
981
987
|
|
|
982
988
|
componentWillUnmount() {
|
|
983
989
|
window.removeEventListener("message", this.handleFrameTasks);
|
|
984
|
-
|
|
985
|
-
|
|
990
|
+
// When using local templates (e.g. SMS fallback selector), do not clear global store or background RCS list is wiped
|
|
991
|
+
const useLocalTemplates = get(
|
|
992
|
+
this.props,
|
|
993
|
+
'localTemplatesConfig.useLocalTemplates',
|
|
994
|
+
get(this.props, 'useLocalTemplates', false),
|
|
995
|
+
);
|
|
996
|
+
if (!useLocalTemplates) {
|
|
997
|
+
this.props.actions.resetTemplateStoreData();
|
|
998
|
+
this.props.globalActions.clearMetaEntities();
|
|
999
|
+
}
|
|
986
1000
|
// Clear any pending timeouts to prevent memory leaks
|
|
987
1001
|
if (this._clearEditTimeout) {
|
|
988
1002
|
clearTimeout(this._clearEditTimeout);
|
|
@@ -1767,12 +1781,20 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
1767
1781
|
}
|
|
1768
1782
|
|
|
1769
1783
|
filterRcsTemplates = (templates) => {
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1784
|
+
const selectedRcsAccountName = this.props?.Templates?.selectedRcsAccount?.name || '';
|
|
1785
|
+
const hostName = this.state?.hostName;
|
|
1786
|
+
let nextTemplates = templates || [];
|
|
1787
|
+
if (selectedRcsAccountName) {
|
|
1788
|
+
nextTemplates = nextTemplates.filter(
|
|
1789
|
+
(t) => get(t, 'versions.base.content.RCS.rcsContent.accountName', '') === selectedRcsAccountName
|
|
1790
|
+
);
|
|
1774
1791
|
}
|
|
1775
|
-
|
|
1792
|
+
if (!this.props.isFullMode && hostName !== HOST_INFOBIP) {
|
|
1793
|
+
return nextTemplates.filter(
|
|
1794
|
+
(t) => get(t, 'versions.base.content.RCS.rcsContent.cardContent[0].Status', 'unavailable') === RCS_STATUSES.approved
|
|
1795
|
+
);
|
|
1796
|
+
}
|
|
1797
|
+
return nextTemplates;
|
|
1776
1798
|
}
|
|
1777
1799
|
|
|
1778
1800
|
filterZaloTemplates = (templates) => {
|
|
@@ -1947,7 +1969,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
1947
1969
|
style={{ marginRight: "16px" }}
|
|
1948
1970
|
type="eye"
|
|
1949
1971
|
onClick={() => {
|
|
1950
|
-
if (!this.props.isFullMode || this.props.isDltFromRcs) {
|
|
1972
|
+
if (!this.props.isFullMode || this.props.isDltFromRcs || this.props.isSmsFallbackFromRcs) {
|
|
1951
1973
|
if (!get(template, "versions.base.content.zalo.previewUrl", "")) {
|
|
1952
1974
|
this.setState({ zaloPreviewItemId: template?._id });
|
|
1953
1975
|
}
|
|
@@ -2407,13 +2429,14 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2407
2429
|
templateData.title = (
|
|
2408
2430
|
<CapRow>
|
|
2409
2431
|
<CapLabel className="whatsapp-rcs-template-name">{name}</CapLabel>
|
|
2410
|
-
<CapRow type="flex" align="middle" className="rcs-status-container zalo-status-color">
|
|
2432
|
+
{this.state.hostName !== HOST_INFOBIP && <CapRow type="flex" align="middle" className="rcs-status-container zalo-status-color">
|
|
2411
2433
|
<CapStatus
|
|
2412
2434
|
type={statusDisplay}
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2435
|
+
text={statusDisplay && this.props.intl.formatMessage(rcsMessages?.[`${statusDisplay}_STATUS`])}
|
|
2436
|
+
labelType="label3"
|
|
2437
|
+
/>
|
|
2438
|
+
</CapRow>
|
|
2439
|
+
}
|
|
2417
2440
|
</CapRow>
|
|
2418
2441
|
);
|
|
2419
2442
|
|
|
@@ -2937,6 +2960,7 @@ return (<div>
|
|
|
2937
2960
|
let routeParams = {};
|
|
2938
2961
|
const {fbAdManager} = this.props;
|
|
2939
2962
|
const isLibraryMode = this.isEnabledInLibraryModule("callCreateFromProps");
|
|
2963
|
+
|
|
2940
2964
|
if (!isLibraryMode) {
|
|
2941
2965
|
timeTracker.startTimer(CHANNEL_CREATE_TRACK_MAPPING[channel]);
|
|
2942
2966
|
}
|
|
@@ -3282,6 +3306,13 @@ return (<div>
|
|
|
3282
3306
|
this.setState({modeType});
|
|
3283
3307
|
}
|
|
3284
3308
|
const { _id: id } = template;
|
|
3309
|
+
const {
|
|
3310
|
+
localTemplatesConfig,
|
|
3311
|
+
fbAdManager,
|
|
3312
|
+
isDltFromRcs,
|
|
3313
|
+
isSmsFallbackFromRcs,
|
|
3314
|
+
onSelectTemplate,
|
|
3315
|
+
} = this.props;
|
|
3285
3316
|
const type = this.props.location.query.type;
|
|
3286
3317
|
const module = this.props.location.query.module;
|
|
3287
3318
|
const isLanguageSupport = (this.props.location.query.isLanguageSupport) ? this.props.location.query.isLanguageSupport : false;
|
|
@@ -3367,10 +3398,12 @@ return (<div>
|
|
|
3367
3398
|
}
|
|
3368
3399
|
if (this.isEnabledInLibraryModule("callSelectFromProps")) {
|
|
3369
3400
|
let data = id;
|
|
3370
|
-
if (
|
|
3401
|
+
if (localTemplatesConfig?.useLocalTemplates) {
|
|
3402
|
+
data = template;
|
|
3403
|
+
} else if (fbAdManager || isDltFromRcs || isSmsFallbackFromRcs) {
|
|
3371
3404
|
data = this.selectTemplate(id);
|
|
3372
3405
|
}
|
|
3373
|
-
|
|
3406
|
+
onSelectTemplate(data, fbAdManager);
|
|
3374
3407
|
} else {
|
|
3375
3408
|
timeTracker.startTimer(CHANNEL_EDIT_TRACK_MAPPING[this.state.channel.toLowerCase()]);
|
|
3376
3409
|
if (this.state.channel.toLowerCase() === 'ebill') {
|
|
@@ -4186,9 +4219,14 @@ return (<div>
|
|
|
4186
4219
|
const isWechatEmbedded = !this.props.isFullMode && channel.toUpperCase() === WECHAT;
|
|
4187
4220
|
const channelLowerCase = (channel || '').toLowerCase();
|
|
4188
4221
|
const isTraiDltFeature = this.checkDLTfeatureEnable();
|
|
4189
|
-
|
|
4190
4222
|
const createButton =
|
|
4191
|
-
(
|
|
4223
|
+
(
|
|
4224
|
+
(
|
|
4225
|
+
channelLowerCase === WHATSAPP_LOWERCASE
|
|
4226
|
+
|| channelLowerCase === RCS_LOWERCASE
|
|
4227
|
+
)
|
|
4228
|
+
&& !this.props.isFullMode
|
|
4229
|
+
)
|
|
4192
4230
|
? (
|
|
4193
4231
|
<CapLink
|
|
4194
4232
|
onClick={this.openCreativesFullMode}
|
|
@@ -4214,17 +4252,23 @@ return (<div>
|
|
|
4214
4252
|
if (([WHATSAPP_LOWERCASE, ZALO_LOWERCASE, RCS_LOWERCASE].includes(this.state?.channel?.toLocaleLowerCase()) && isEmpty(this.state?.hostName))) {
|
|
4215
4253
|
isfilterContentVisisble = false;
|
|
4216
4254
|
}
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4255
|
+
|
|
4256
|
+
const useLocalTemplates = this.props.localTemplatesConfig?.useLocalTemplates;
|
|
4257
|
+
const builtFilterContent = ((isfilterContentVisisble || [WECHAT, MOBILE_PUSH, INAPP].includes(this.state.channel.toUpperCase())) && (
|
|
4258
|
+
<div className="action-container">
|
|
4259
|
+
<div className="action-container__toolbar-row">
|
|
4260
|
+
{isfilterContentVisisble ? (
|
|
4261
|
+
<CapInput.Search
|
|
4262
|
+
className="search-text"
|
|
4263
|
+
placeholder={this.props.intl.formatMessage(messages.searchText)}
|
|
4264
|
+
value={this.state.searchText}
|
|
4265
|
+
onChange={(e) => this.searchTemplate(e.target.value, this.state.channel)}
|
|
4266
|
+
onSearch={() => this.searchTemplate(this.state.searchText, this.state.channel)}
|
|
4267
|
+
onClear={() => this.searchTemplate('', this.state.channel)}
|
|
4268
|
+
onScroll={(e) => e.stopPropagation()}
|
|
4269
|
+
disabled={this.checkSearchDisabled()}
|
|
4270
|
+
/>
|
|
4271
|
+
) : null}
|
|
4228
4272
|
{
|
|
4229
4273
|
channel.toUpperCase() === WECHAT && <CapRadio.CapRadioGroup className="wechat-filters" defaultValue={wechatFilter} onChange={this.setWechatFilter}>
|
|
4230
4274
|
<CapRadio.Button value={WECHAT_FILTERS.ALL}><CapLabel type="label2">
|
|
@@ -4370,20 +4414,27 @@ return (<div>
|
|
|
4370
4414
|
</div>
|
|
4371
4415
|
)
|
|
4372
4416
|
}
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
{
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4417
|
+
</div>
|
|
4418
|
+
<div>
|
|
4419
|
+
<div className="action-container__create-row">
|
|
4420
|
+
{
|
|
4421
|
+
this.state?.channel?.toLowerCase() === WHATSAPP_LOWERCASE && (isWhatsappCountExeeded) ? (
|
|
4422
|
+
<CapTooltip title={whatsappCountExceedText}>
|
|
4423
|
+
<div className="button-disabled-tooltip-wrapper">
|
|
4424
|
+
{createButton}
|
|
4425
|
+
</div>
|
|
4426
|
+
</CapTooltip>
|
|
4427
|
+
)
|
|
4428
|
+
: isfilterContentVisisble && !isWechatEmbedded && !this.props.isDltFromRcs && !this.props.isSmsFallbackFromRcs && createButton
|
|
4429
|
+
}
|
|
4430
|
+
</div>
|
|
4431
|
+
</div>
|
|
4384
4432
|
</div>
|
|
4385
|
-
|
|
4386
|
-
|
|
4433
|
+
));
|
|
4434
|
+
const localTemplatesFilterContent = get(this.props, 'localTemplatesConfig.localTemplatesFilterContent', null);
|
|
4435
|
+
const filterContent = (useLocalTemplates && localTemplatesFilterContent) != null
|
|
4436
|
+
? localTemplatesFilterContent
|
|
4437
|
+
: builtFilterContent;
|
|
4387
4438
|
let htmlPreviewContent = "";
|
|
4388
4439
|
if (this.state.channel.toLowerCase() === 'ebill') {
|
|
4389
4440
|
htmlPreviewContent = this.state.previewTemplate && this.state.previewTemplate.versions && this.state.previewTemplate.versions.base && this.state.previewTemplate.versions.base['ebill-editor'];
|
|
@@ -4393,7 +4444,10 @@ return (<div>
|
|
|
4393
4444
|
|
|
4394
4445
|
|
|
4395
4446
|
const creativesParams = this.getCreativesParams();
|
|
4396
|
-
const templates =
|
|
4447
|
+
const templates = useLocalTemplates
|
|
4448
|
+
? (this.props.localTemplatesConfig?.localTemplates || [])
|
|
4449
|
+
: (this.props.TemplatesList || []);
|
|
4450
|
+
const isLoadingWhenLocal = useLocalTemplates && !!this.props.localTemplatesConfig?.localTemplatesLoading;
|
|
4397
4451
|
const {route} = this.props;
|
|
4398
4452
|
const loadingTipMap = {
|
|
4399
4453
|
sendingFile: 'uploadingFile',
|
|
@@ -4408,9 +4462,11 @@ return (<div>
|
|
|
4408
4462
|
(deleteRcsTemplateInProgress && 'deletingTemplate') ||
|
|
4409
4463
|
(this.props.EmailCreate.duplicateTemplateInProgress && 'duplicatingTemplate');
|
|
4410
4464
|
|
|
4411
|
-
const loadingTip =
|
|
4465
|
+
const loadingTip = useLocalTemplates && this.props.localTemplatesConfig?.localTemplatesLoadingTip
|
|
4466
|
+
? this.props.localTemplatesConfig.localTemplatesLoadingTip
|
|
4467
|
+
: (messages[loadingTipIntl] ? this.props.intl.formatMessage(messages[loadingTipIntl]) : this.props.intl.formatMessage(messages.gettingAllTemplates));
|
|
4412
4468
|
const showNoTemplatesFoundZalo = this.state.channel.toUpperCase() === ZALO && isEmpty(this.state.searchedZaloTemplates) && this.state.searchingZaloTemplate;
|
|
4413
|
-
const showNoTemplatesFoundOther = ![ZALO].includes(this.state.channel.toUpperCase()) && isEmpty(
|
|
4469
|
+
const showNoTemplatesFoundOther = ![ZALO].includes(this.state.channel.toUpperCase()) && isEmpty(templates) && (useLocalTemplates ? !isLoadingWhenLocal : !this.props.Templates.getAllTemplatesInProgress) && (useLocalTemplates || !isEmpty(this.state.searchText));
|
|
4414
4470
|
const showNoTemplatesFound = showNoTemplatesFoundZalo || showNoTemplatesFoundOther;
|
|
4415
4471
|
|
|
4416
4472
|
return (
|
|
@@ -4442,7 +4498,7 @@ return (<div>
|
|
|
4442
4498
|
/>
|
|
4443
4499
|
) : null}
|
|
4444
4500
|
|
|
4445
|
-
{channel.toLowerCase() === RCS_LOWERCASE && !isFullMode ? (
|
|
4501
|
+
{channel.toLowerCase() === RCS_LOWERCASE && !isFullMode && this.state?.hostName !== HOST_INFOBIP ? (
|
|
4446
4502
|
<CapInfoNote
|
|
4447
4503
|
message={formatMessage(messages.rcsOnlyApprovedTemplates)}
|
|
4448
4504
|
/>
|
|
@@ -4455,22 +4511,22 @@ return (<div>
|
|
|
4455
4511
|
) : null}
|
|
4456
4512
|
<CapRow>
|
|
4457
4513
|
<Pagination
|
|
4458
|
-
templateInProgress={
|
|
4459
|
-
this.props.Templates.getAllTemplatesInProgress
|
|
4460
|
-
}
|
|
4514
|
+
templateInProgress={useLocalTemplates ? isLoadingWhenLocal : this.props.Templates.getAllTemplatesInProgress}
|
|
4461
4515
|
onPageChange={
|
|
4462
|
-
templates.length
|
|
4516
|
+
templates.length
|
|
4517
|
+
? (useLocalTemplates ? (this.props.localTemplatesConfig?.localTemplatesOnPageChange || (() => {})) : this.onPaginationChange)
|
|
4518
|
+
: () => {}
|
|
4463
4519
|
}
|
|
4464
4520
|
>
|
|
4465
4521
|
{this.getTemplateDataForGrid({
|
|
4466
4522
|
previewTemplateId: this.state.zaloPreviewItemId,
|
|
4467
|
-
isLoading,
|
|
4468
|
-
isInitialLoading,
|
|
4523
|
+
isLoading: useLocalTemplates ? isLoadingWhenLocal : isLoading,
|
|
4524
|
+
isInitialLoading: useLocalTemplates ? isLoadingWhenLocal && templates.length === 0 : isInitialLoading,
|
|
4469
4525
|
loadingTip,
|
|
4470
4526
|
channel: this.state.channel,
|
|
4471
4527
|
templates: this.state.searchingZaloTemplate
|
|
4472
4528
|
? this.state.searchedZaloTemplates
|
|
4473
|
-
:
|
|
4529
|
+
: templates,
|
|
4474
4530
|
filterContent,
|
|
4475
4531
|
handlers: {
|
|
4476
4532
|
handlePreviewClick: this.handlePreviewClick,
|
|
@@ -4653,6 +4709,15 @@ Templates.propTypes = {
|
|
|
4653
4709
|
WebPush: PropTypes.object,
|
|
4654
4710
|
smsRegister: PropTypes.any,
|
|
4655
4711
|
isDltFromRcs: PropTypes.bool,
|
|
4712
|
+
isSmsFallbackFromRcs: PropTypes.bool,
|
|
4713
|
+
localTemplatesConfig: PropTypes.shape({
|
|
4714
|
+
useLocalTemplates: PropTypes.bool,
|
|
4715
|
+
localTemplates: PropTypes.arrayOf(PropTypes.object),
|
|
4716
|
+
localTemplatesLoading: PropTypes.bool,
|
|
4717
|
+
localTemplatesLoadingTip: PropTypes.string,
|
|
4718
|
+
localTemplatesFilterContent: PropTypes.node,
|
|
4719
|
+
localTemplatesOnPageChange: PropTypes.func,
|
|
4720
|
+
}),
|
|
4656
4721
|
};
|
|
4657
4722
|
|
|
4658
4723
|
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 { schema, normalize } from 'normalizr';
|
|
@@ -7,31 +7,69 @@ import * as Api from '../../services/api';
|
|
|
7
7
|
import * as types from './constants';
|
|
8
8
|
import { saveCdnConfigs, removeAllCdnLocalStorageItems } from '../../utils/cdnTransformation';
|
|
9
9
|
import { COPY_OF } from '../../constants/unified';
|
|
10
|
+
import { fetchSmsTemplatesFromQuery } from './utils/smsTemplatesListApi';
|
|
10
11
|
import { ZALO_TEMPLATE_INFO_REQUEST } from '../Zalo/constants';
|
|
11
12
|
import { getTemplateInfoById } from '../Zalo/saga';
|
|
12
|
-
|
|
13
|
-
export function*
|
|
13
|
+
|
|
14
|
+
export function* getLocalSmsTemplates(action) {
|
|
14
15
|
try {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
const fetched = yield call(
|
|
17
|
+
fetchSmsTemplatesFromQuery,
|
|
18
|
+
action.queryParams,
|
|
19
|
+
action.intlCopyOf,
|
|
20
|
+
);
|
|
21
|
+
if (typeof action.onSuccess === 'function') {
|
|
22
|
+
yield call(action.onSuccess, fetched);
|
|
23
|
+
}
|
|
24
|
+
} catch (error) {
|
|
25
|
+
if (typeof action.onFailure === 'function') {
|
|
26
|
+
yield call(action.onFailure, error);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function* getAllTemplates(action) {
|
|
32
|
+
try {
|
|
33
|
+
if (action.channel && String(action.channel).toLowerCase() === 'sms') {
|
|
34
|
+
const fetched = yield call(
|
|
35
|
+
fetchSmsTemplatesFromQuery,
|
|
36
|
+
action.queryParams,
|
|
37
|
+
action.intlCopyOf,
|
|
38
|
+
);
|
|
39
|
+
yield put({
|
|
40
|
+
type: types.GET_ALL_TEMPLATES_SUCCESS,
|
|
41
|
+
data: fetched.channelTemplates,
|
|
42
|
+
weCRMTemplate: fetched.weCRMTemplate,
|
|
43
|
+
isReset: get(action, 'queryParams.page') === 1,
|
|
44
|
+
});
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const result = yield call(Api.getAllTemplates, action);
|
|
49
|
+
const channelTemplates = (action.channel === 'wechat')
|
|
50
|
+
? { templates: [...result.response.mapped, ...result.response.richmedia] }
|
|
51
|
+
: result.response;
|
|
52
|
+
if (action.channel === 'wechat' && action.queryParams && action.queryParams.sortBy && action.queryParams.sortBy.toLocaleLowerCase() === ("Most Recent").toLocaleLowerCase()) {
|
|
19
53
|
channelTemplates.templates.sort((a, b) => {
|
|
20
54
|
const dateA = new Date(a.updatedAt);
|
|
21
55
|
const dateB = new Date(b.updatedAt);
|
|
22
56
|
return dateB - dateA;
|
|
23
57
|
});
|
|
24
|
-
} else if (
|
|
58
|
+
} else if (action.channel === 'wechat' && action.queryParams && action.queryParams.sortBy && action.queryParams.sortBy.toLocaleLowerCase() === ("Alphabetically").toLocaleLowerCase()) {
|
|
25
59
|
channelTemplates.templates.sort((a, b) => b.name - a.name);
|
|
26
60
|
}
|
|
27
|
-
|
|
28
|
-
if (channel.intlCopyOf && channelTemplates?.templates) {
|
|
61
|
+
if (action.intlCopyOf && channelTemplates?.templates) {
|
|
29
62
|
channelTemplates.templates = channelTemplates.templates.map((template) => ({
|
|
30
63
|
...template,
|
|
31
|
-
name: template.name.replace(new RegExp(COPY_OF, 'g'),
|
|
64
|
+
name: template.name.replace(new RegExp(COPY_OF, 'g'), action.intlCopyOf),
|
|
32
65
|
}));
|
|
33
66
|
}
|
|
34
|
-
yield put({
|
|
67
|
+
yield put({
|
|
68
|
+
type: types.GET_ALL_TEMPLATES_SUCCESS,
|
|
69
|
+
data: channelTemplates,
|
|
70
|
+
weCRMTemplate: result.response.unMapped,
|
|
71
|
+
isReset: get(action, 'queryParams.page') === 1,
|
|
72
|
+
});
|
|
35
73
|
} catch (error) {
|
|
36
74
|
yield put({ type: types.GET_ALL_TEMPLATES_FAILURE, error });
|
|
37
75
|
}
|
|
@@ -205,6 +243,11 @@ export function* watchGetAllTemplates() {
|
|
|
205
243
|
yield takeLatest(types.GET_ALL_TEMPLATES_REQUEST, getAllTemplates);
|
|
206
244
|
}
|
|
207
245
|
|
|
246
|
+
|
|
247
|
+
export function* watchGetLocalSmsTemplates() {
|
|
248
|
+
yield takeEvery(types.GET_LOCAL_SMS_TEMPLATES_REQUEST, getLocalSmsTemplates);
|
|
249
|
+
}
|
|
250
|
+
|
|
208
251
|
export function* watchDeleteTemplate() {
|
|
209
252
|
yield takeLatest(types.DELETE_TEMPLATE_REQUEST, deleteTemplate);
|
|
210
253
|
}
|
|
@@ -258,6 +301,7 @@ export function* watchForGetTemplateInfoById() {
|
|
|
258
301
|
// All sagas to be loaded
|
|
259
302
|
export default [
|
|
260
303
|
watchGetAllTemplates,
|
|
304
|
+
watchGetLocalSmsTemplates,
|
|
261
305
|
watchDeleteTemplate,
|
|
262
306
|
watchDeleteRcsTemplate,
|
|
263
307
|
watchGetUserList,
|
|
@@ -274,6 +318,7 @@ export default [
|
|
|
274
318
|
export function* v2TemplateSaga() {
|
|
275
319
|
yield all([
|
|
276
320
|
watchGetAllTemplates(),
|
|
321
|
+
watchGetLocalSmsTemplates(),
|
|
277
322
|
watchDeleteTemplate(),
|
|
278
323
|
watchDeleteRcsTemplate(),
|
|
279
324
|
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
|
+
});
|