@capillarytech/creatives-library 8.0.353-alpha.5 → 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/PreviewHeader.js +0 -17
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +157 -15
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +346 -146
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +138 -48
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
- package/v2Components/CommonTestAndPreview/constants.js +38 -4
- package/v2Components/CommonTestAndPreview/index.js +691 -235
- 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/PreviewHeader.test.js +0 -159
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -256
- package/v2Components/CommonTestAndPreview/tests/constants.test.js +1 -2
- package/v2Components/CommonTestAndPreview/tests/index.test.js +132 -198
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +36 -26
- package/v2Components/FormBuilder/index.js +11 -6
- 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 +15 -3
- 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/App/constants.js +0 -3
- 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 +322 -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/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 +122 -120
- 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/WebPush/Create/index.js +8 -91
- package/v2Containers/WebPush/Create/index.scss +0 -7
- package/v2Containers/Whatsapp/index.js +3 -20
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
- package/v2Components/CommonTestAndPreview/UnifiedPreview/WebPushPreviewContent.js +0 -169
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/WebPushPreviewContent.test.js +0 -522
- package/v2Containers/App/tests/constants.test.js +0 -61
- package/v2Containers/Templates/tests/webpush.test.js +0 -375
- package/v2Containers/WebPush/Create/tests/getTemplateContent.test.js +0 -338
- package/v2Containers/WebPush/Create/tests/testAndPreviewIntegration.test.js +0 -325
|
@@ -10,8 +10,8 @@ import { connect } from 'react-redux';
|
|
|
10
10
|
import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
|
|
11
11
|
import { createStructuredSelector } from 'reselect';
|
|
12
12
|
import { bindActionCreators, compose } from 'redux';
|
|
13
|
-
import { CapTab, CapCustomCard, CapButton, CapHeader,
|
|
14
|
-
import { find, get } from 'lodash';
|
|
13
|
+
import { CapTab, CapCustomCard, CapButton, CapHeader, CapIcon, CapSpin, CapTooltip } from '@capillarytech/cap-ui-library';
|
|
14
|
+
import { find, get, pick } from 'lodash';
|
|
15
15
|
import Helmet from 'react-helmet';
|
|
16
16
|
|
|
17
17
|
import { UserIsAuthenticated } from '../../utils/authWrapper';
|
|
@@ -36,13 +36,14 @@ import { makeSelectAuthenticated, selectCurrentOrgDetails } from "../../v2Contai
|
|
|
36
36
|
import {
|
|
37
37
|
CALL_TASK,
|
|
38
38
|
COMMON_CHANNELS,
|
|
39
|
+
LOCAL_TEMPLATE_CONFIG_KEYS_FOR_PICK,
|
|
39
40
|
LOYALTY_SUPPORTED_ACTION,
|
|
40
41
|
MOBILE_PUSH,
|
|
41
42
|
NORMALIZED_CHANNEL_ALIASES,
|
|
42
43
|
SMS,
|
|
43
44
|
} from "../CreativesContainer/constants";
|
|
44
45
|
|
|
45
|
-
const {CapCustomCardList} = CapCustomCard;
|
|
46
|
+
const { CapCustomCardList } = CapCustomCard;
|
|
46
47
|
|
|
47
48
|
const StyledCapTab = withStyles(CapTab, CapTabStyle);
|
|
48
49
|
export class TemplatesV2 extends React.Component { // eslint-disable-line react/prefer-stateless-function
|
|
@@ -119,9 +120,9 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
|
|
|
119
120
|
return !normalizedChannelsToHideSet.has(paneKey);
|
|
120
121
|
});
|
|
121
122
|
|
|
122
|
-
if (isFullMode) {
|
|
123
|
-
filteredPanes.push({ content: <div></div>, tab: intl.formatMessage(messages.gallery), key:
|
|
124
|
-
} else {
|
|
123
|
+
if (isFullMode && !normalizedChannelsToHideSet.has(normalizeChannel(ASSETS))) {
|
|
124
|
+
filteredPanes.push({ content: <div></div>, tab: intl.formatMessage(messages.gallery), key: ASSETS });
|
|
125
|
+
} else if (!isFullMode) {
|
|
125
126
|
// Add special-mode panes only when not hidden (use normalized checks)
|
|
126
127
|
if (!normalizedChannelsToHideSet.has(CALL_TASK.toLowerCase())) {
|
|
127
128
|
filteredPanes.push({ content: <div></div>, tab: intl.formatMessage(messages.callTask), key: CALL_TASK.toLowerCase() });
|
|
@@ -222,7 +223,8 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
|
|
|
222
223
|
this.setState({selectedChannel: nextProps.channel, panes });
|
|
223
224
|
}
|
|
224
225
|
}
|
|
225
|
-
|
|
226
|
+
|
|
227
|
+
getTemplateDataForGrid = ({ templates, handlers, filterContent, channel, isLoading, loadingTip }) => {
|
|
226
228
|
const currentChannel = channel.toUpperCase();
|
|
227
229
|
const cardDataList = templates.map((template) => {
|
|
228
230
|
const templateData =
|
|
@@ -248,7 +250,8 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
|
|
|
248
250
|
</CapSpin>
|
|
249
251
|
|
|
250
252
|
</div>);
|
|
251
|
-
}
|
|
253
|
+
};
|
|
254
|
+
|
|
252
255
|
getGalleryComponent = (location) => <Gallery location={location} isFullMode={this.props.isFullMode}/>
|
|
253
256
|
getCallTaskComponent = () => (
|
|
254
257
|
<CallTask
|
|
@@ -312,6 +315,29 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
|
|
|
312
315
|
if (messageStrategy !== "X_ENGAGE" && channel === 'facebook' && !isFullMode) {
|
|
313
316
|
return this.getFacebookComponent();
|
|
314
317
|
}
|
|
318
|
+
const localConfig = this.props.localTemplatesConfig || pick(this.props, LOCAL_TEMPLATE_CONFIG_KEYS_FOR_PICK);
|
|
319
|
+
const useLocalTemplates = localConfig.useLocalTemplates;
|
|
320
|
+
if (useLocalTemplates && channel === (this.props.channel || 'sms')) {
|
|
321
|
+
// Reuse full Templates component (same UI as Redux flow) with local data only
|
|
322
|
+
const location = { pathname: `/${channel}`, search: '', query: !this.props.isFullMode ? { type: 'embedded', module: 'library' } : {} };
|
|
323
|
+
return (
|
|
324
|
+
<Templates
|
|
325
|
+
key={`${channel}-local`}
|
|
326
|
+
location={location}
|
|
327
|
+
route={{ name: channel }}
|
|
328
|
+
router={this.props.router}
|
|
329
|
+
isFullMode={this.props.isFullMode}
|
|
330
|
+
createNew={this.props.createNew}
|
|
331
|
+
onSelectTemplate={this.props.onSelectTemplate}
|
|
332
|
+
handlePeviewTemplate={this.props.handlePeviewTemplate}
|
|
333
|
+
messageStrategy={this.props.messageStrategy}
|
|
334
|
+
smsRegister={this.props.smsRegister}
|
|
335
|
+
hideTestAndPreviewBtn={this.props.hideTestAndPreviewBtn}
|
|
336
|
+
localTemplatesConfig={localConfig}
|
|
337
|
+
/>
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
315
341
|
const location = {pathname: `/${channel}`, search: '', query};
|
|
316
342
|
switch (channel) {
|
|
317
343
|
case 'call_task':
|
|
@@ -361,29 +387,55 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
|
|
|
361
387
|
}
|
|
362
388
|
render() {
|
|
363
389
|
const { isFullMode, className, cap = {}, Global = {}} = this.props;
|
|
390
|
+
const useLocalTemplates = get(this.props, 'localTemplatesConfig.useLocalTemplates', false);
|
|
364
391
|
const { accessiblePermissions = []} = cap.user || Global.user || {};
|
|
365
392
|
let isCreativeAccessible = true;
|
|
366
393
|
if (!accessiblePermissions.includes(CREATIVES_UI_VIEW)) {
|
|
367
394
|
isCreativeAccessible = false;
|
|
368
395
|
}
|
|
396
|
+
// Recompute active pane content every render so local-list mode updates
|
|
397
|
+
// (templates/loading/search UI) are not stuck with the initial cached pane.
|
|
398
|
+
const panes = this.setChannelContent(this.state.selectedChannel, this.state.panes);
|
|
399
|
+
const hideChannelTabsForLocalSms = useLocalTemplates && panes.length === 1;
|
|
400
|
+
const activeLocalPane = hideChannelTabsForLocalSms
|
|
401
|
+
? (panes.find(
|
|
402
|
+
(p) => String(p.key).toLowerCase() === String(this.state.selectedChannel).toLowerCase(),
|
|
403
|
+
) || panes[0])
|
|
404
|
+
: null;
|
|
369
405
|
return (
|
|
370
406
|
!isCreativeAccessible ? <AccessForbidden /> : (
|
|
371
|
-
<div
|
|
372
|
-
{isFullMode
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
panes={this.state.panes}
|
|
382
|
-
onChange={this.channelChange}
|
|
383
|
-
activeKey={this.state.selectedChannel}
|
|
384
|
-
defaultActiveKey={this.state.selectedChannel}
|
|
385
|
-
isFullMode={isFullMode}
|
|
407
|
+
<div
|
|
408
|
+
className={`${className} creatives-templates-container ${isFullMode ? 'fullmode' : 'library-mode'}${useLocalTemplates ? ' creatives-templates-container--local-sms' : ''}`}
|
|
409
|
+
data-testid="cap-wrapper"
|
|
410
|
+
>
|
|
411
|
+
{isFullMode && !useLocalTemplates && (
|
|
412
|
+
<Helmet
|
|
413
|
+
title={this.props.intl.formatMessage(messages.creatives)}
|
|
414
|
+
meta={[
|
|
415
|
+
{ name: 'description', content: this.props.intl.formatMessage(messages.creativesDesc) },
|
|
416
|
+
]}
|
|
386
417
|
/>
|
|
418
|
+
)}
|
|
419
|
+
<div className="component-wrapper">
|
|
420
|
+
{isFullMode && (
|
|
421
|
+
<CapHeader
|
|
422
|
+
title={<FormattedMessage {...messages.creatives} />}
|
|
423
|
+
{...(!useLocalTemplates && {
|
|
424
|
+
description: <FormattedMessage {...messages.creativesDesc} />,
|
|
425
|
+
})}
|
|
426
|
+
/>
|
|
427
|
+
)}
|
|
428
|
+
{hideChannelTabsForLocalSms ? (
|
|
429
|
+
<div className="templates-v2-local-sms-pane">{activeLocalPane?.content}</div>
|
|
430
|
+
) : (
|
|
431
|
+
<StyledCapTab
|
|
432
|
+
panes={panes}
|
|
433
|
+
onChange={this.channelChange}
|
|
434
|
+
activeKey={this.state.selectedChannel}
|
|
435
|
+
defaultActiveKey={this.state.selectedChannel}
|
|
436
|
+
isFullMode={isFullMode}
|
|
437
|
+
/>
|
|
438
|
+
)}
|
|
387
439
|
</div>
|
|
388
440
|
</div>
|
|
389
441
|
)
|
|
@@ -415,6 +467,17 @@ TemplatesV2.propTypes = {
|
|
|
415
467
|
currentOrgDetails: PropTypes.object,
|
|
416
468
|
restrictPersonalization: PropTypes.bool,
|
|
417
469
|
isAnonymousType: PropTypes.bool,
|
|
470
|
+
// Optional: reuse grid UI with local template list (e.g. SMS fallback). Pass object or same keys as individual props.
|
|
471
|
+
localTemplatesConfig: PropTypes.shape({
|
|
472
|
+
useLocalTemplates: PropTypes.bool,
|
|
473
|
+
localTemplates: PropTypes.arrayOf(PropTypes.object),
|
|
474
|
+
localTemplatesLoading: PropTypes.bool,
|
|
475
|
+
localTemplatesLoadingTip: PropTypes.string,
|
|
476
|
+
localTemplatesFilterContent: PropTypes.node,
|
|
477
|
+
localTemplatesFooterContent: PropTypes.node,
|
|
478
|
+
localTemplatesOnPageChange: PropTypes.func,
|
|
479
|
+
localTemplatesUseSkeleton: PropTypes.bool,
|
|
480
|
+
}),
|
|
418
481
|
};
|
|
419
482
|
|
|
420
483
|
TemplatesV2.defaultProps = {
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedded SMS template list: localTemplatesConfig + SMS-only channel visibility (RCS SMS fallback).
|
|
3
|
+
*/
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { injectIntl } from 'react-intl';
|
|
6
|
+
import '@testing-library/jest-dom';
|
|
7
|
+
import cloneDeep from 'lodash/cloneDeep';
|
|
8
|
+
import { Provider } from 'react-redux';
|
|
9
|
+
import { configureStore } from '@capillarytech/vulcan-react-sdk/utils';
|
|
10
|
+
import history from '../../../utils/history';
|
|
11
|
+
import { initialReducer } from '../../../initialReducer';
|
|
12
|
+
import { render, screen } from '../../../utils/test-utils';
|
|
13
|
+
import { TemplatesV2 } from '../index';
|
|
14
|
+
import { Templates, authData, currentOrgDetails as currentOrgDetailsMock } from './mockData';
|
|
15
|
+
import { CHANNELS_TO_HIDE_FOR_SMS_ONLY } from '../../../v2Components/SmsFallback/constants';
|
|
16
|
+
|
|
17
|
+
const mockTemplates = jest.fn(() => <div data-testid="templates-mock">Templates</div>);
|
|
18
|
+
jest.mock('v2Containers/Templates', () => ({
|
|
19
|
+
__esModule: true,
|
|
20
|
+
default: (props) => mockTemplates(props),
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
jest.mock('../../../utils/authWrapper', () => ({
|
|
24
|
+
UserIsAuthenticated: jest.fn((config) => config),
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
const ComponentToRender = injectIntl(TemplatesV2);
|
|
28
|
+
const renderComponent = (p) => {
|
|
29
|
+
const store = configureStore({}, initialReducer, history);
|
|
30
|
+
return render(
|
|
31
|
+
<Provider store={store}>
|
|
32
|
+
<ComponentToRender {...p} />
|
|
33
|
+
</Provider>,
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
describe('TemplatesV2 local SMS templates (embedded)', () => {
|
|
38
|
+
const templateActions = {
|
|
39
|
+
templateActions: jest.fn(),
|
|
40
|
+
deleteTemplate: jest.fn(),
|
|
41
|
+
getAccountsSettings: jest.fn(),
|
|
42
|
+
getAllTemplates: jest.fn(),
|
|
43
|
+
getCdnTransformationConfig: jest.fn(),
|
|
44
|
+
getDefaultBeeTemplates: jest.fn(),
|
|
45
|
+
getSenderDetails: jest.fn(),
|
|
46
|
+
getTemplateDetails: jest.fn(),
|
|
47
|
+
getUserList: jest.fn(),
|
|
48
|
+
getWeCrmAccounts: jest.fn(),
|
|
49
|
+
handleHtmlUpload: jest.fn(),
|
|
50
|
+
handleZipUpload: jest.fn(),
|
|
51
|
+
resetAccount: jest.fn(),
|
|
52
|
+
resetTemplate: jest.fn(),
|
|
53
|
+
resetTemplateData: jest.fn(),
|
|
54
|
+
resetTemplateStoreData: jest.fn(),
|
|
55
|
+
resetUploadData: jest.fn(),
|
|
56
|
+
setBEETemplate: jest.fn(),
|
|
57
|
+
setChannelAccount: jest.fn(),
|
|
58
|
+
setEdmTemplate: jest.fn(),
|
|
59
|
+
setFacebookAccount: jest.fn(),
|
|
60
|
+
setViberAccount: jest.fn(),
|
|
61
|
+
setWeChatAccount: jest.fn(),
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const baseProps = {
|
|
65
|
+
cap: {
|
|
66
|
+
user: { accessiblePermissions: ['CREATIVES_UI_VIEW'] },
|
|
67
|
+
},
|
|
68
|
+
actions: { defaultAction: jest.fn(), getTemplates: jest.fn() },
|
|
69
|
+
Templates,
|
|
70
|
+
TemplatesList: Templates?.templates,
|
|
71
|
+
authData,
|
|
72
|
+
templateActions,
|
|
73
|
+
isFullMode: false,
|
|
74
|
+
className: 'embed-test',
|
|
75
|
+
channel: 'sms',
|
|
76
|
+
channelsToHide: CHANNELS_TO_HIDE_FOR_SMS_ONLY,
|
|
77
|
+
channelsToDisable: [],
|
|
78
|
+
onChannelChange: jest.fn(),
|
|
79
|
+
enableNewChannels: [],
|
|
80
|
+
/** Without JP_LOCALE_HIDE_FEATURE so SMS panes are not stripped to Email/Line/Gallery only */
|
|
81
|
+
currentOrgDetails: {
|
|
82
|
+
...currentOrgDetailsMock,
|
|
83
|
+
accessibleFeatures: (currentOrgDetailsMock.accessibleFeatures || []).filter(
|
|
84
|
+
(f) => f !== 'JP_LOCALE_HIDE_FEATURE',
|
|
85
|
+
),
|
|
86
|
+
},
|
|
87
|
+
location: {
|
|
88
|
+
pathname: 'v2',
|
|
89
|
+
basename: '/creatives/ui/',
|
|
90
|
+
query: {},
|
|
91
|
+
},
|
|
92
|
+
router: { push: jest.fn() },
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
beforeEach(() => {
|
|
96
|
+
mockTemplates.mockClear();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('adds local-sms container class and single-pane layout when only SMS is visible', () => {
|
|
100
|
+
const p = cloneDeep(baseProps);
|
|
101
|
+
p.localTemplatesConfig = {
|
|
102
|
+
useLocalTemplates: true,
|
|
103
|
+
localTemplates: [],
|
|
104
|
+
localTemplatesLoading: false,
|
|
105
|
+
};
|
|
106
|
+
renderComponent(p);
|
|
107
|
+
|
|
108
|
+
const wrapper = screen.getByTestId('cap-wrapper');
|
|
109
|
+
expect(wrapper).toHaveClass('creatives-templates-container--local-sms');
|
|
110
|
+
expect(document.querySelector('.templates-v2-local-sms-pane')).toBeTruthy();
|
|
111
|
+
expect(mockTemplates).toHaveBeenCalled();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('passes localTemplatesConfig into Templates for the SMS pane', () => {
|
|
115
|
+
const localConfig = {
|
|
116
|
+
useLocalTemplates: true,
|
|
117
|
+
localTemplates: [{ _id: '1', name: 'A' }],
|
|
118
|
+
localTemplatesLoading: false,
|
|
119
|
+
};
|
|
120
|
+
const p = cloneDeep(baseProps);
|
|
121
|
+
p.localTemplatesConfig = localConfig;
|
|
122
|
+
renderComponent(p);
|
|
123
|
+
|
|
124
|
+
expect(mockTemplates).toHaveBeenCalled();
|
|
125
|
+
const passed = mockTemplates.mock.calls.find(
|
|
126
|
+
(call) => call[0] && call[0].localTemplatesConfig && call[0].localTemplatesConfig.useLocalTemplates,
|
|
127
|
+
);
|
|
128
|
+
expect(passed).toBeTruthy();
|
|
129
|
+
expect(passed[0].localTemplatesConfig).toMatchObject(localConfig);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -11,7 +11,7 @@ describe('WeChat Template Management Sagas', () => {
|
|
|
11
11
|
describe('getDefaultWeChatTemplates Saga', () => {
|
|
12
12
|
const params = { channel: 'WeChat', queryParams: { type: 'default' } };
|
|
13
13
|
|
|
14
|
-
it
|
|
14
|
+
it('handles fetching default WeChat templates successfully', () => {
|
|
15
15
|
const fakeResponse = {
|
|
16
16
|
response: {
|
|
17
17
|
unMapped: [{ id: 1, name: 'Template One' }],
|
|
@@ -31,7 +31,7 @@ describe('WeChat Template Management Sagas', () => {
|
|
|
31
31
|
.run();
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
-
it
|
|
34
|
+
it('handles failure in fetching default WeChat templates', () => {
|
|
35
35
|
const error = new Error('Fetch failed');
|
|
36
36
|
|
|
37
37
|
return expectSaga(sagas.getDefaultWeChatTemplates, params)
|
|
@@ -48,7 +48,7 @@ describe('WeChat Template Management Sagas', () => {
|
|
|
48
48
|
|
|
49
49
|
describe('createTemplate Saga', () => {
|
|
50
50
|
const template = { name: 'New WeChat Template' };
|
|
51
|
-
test
|
|
51
|
+
test('handles creating a WeChat template successfully', () => {
|
|
52
52
|
const fakeResponse = {
|
|
53
53
|
response: template,
|
|
54
54
|
status: { code: 200 }
|
|
@@ -64,7 +64,7 @@ describe('WeChat Template Management Sagas', () => {
|
|
|
64
64
|
})
|
|
65
65
|
.run();
|
|
66
66
|
});
|
|
67
|
-
test
|
|
67
|
+
test('handles failure when creating a WeChat template', () => {
|
|
68
68
|
const error = new Error('Creation failed');
|
|
69
69
|
return expectSaga(sagas.createTemplate, template)
|
|
70
70
|
.provide([
|
|
@@ -79,7 +79,7 @@ describe('WeChat Template Management Sagas', () => {
|
|
|
79
79
|
});
|
|
80
80
|
|
|
81
81
|
describe('fetchWeCrmAccounts Saga', () => {
|
|
82
|
-
it
|
|
82
|
+
it('handles fetching WeCrm accounts successfully', () => {
|
|
83
83
|
const fakeResponse = {
|
|
84
84
|
response: [{ id: 1, name: 'Account One' }]
|
|
85
85
|
};
|
|
@@ -95,7 +95,7 @@ describe('WeChat Template Management Sagas', () => {
|
|
|
95
95
|
.run();
|
|
96
96
|
});
|
|
97
97
|
|
|
98
|
-
it
|
|
98
|
+
it('handles failure in fetching WeCrm accounts', () => {
|
|
99
99
|
const error = new Error('Fetch failed');
|
|
100
100
|
|
|
101
101
|
return expectSaga(sagas.fetchWeCrmAccounts)
|
|
@@ -113,7 +113,7 @@ describe('WeChat Template Management Sagas', () => {
|
|
|
113
113
|
describe('getTemplateDetails Saga', () => {
|
|
114
114
|
const id = '123';
|
|
115
115
|
|
|
116
|
-
it
|
|
116
|
+
it('handles fetching template details successfully', () => {
|
|
117
117
|
const fakeResponse = {
|
|
118
118
|
response: { id: 123, name: 'Detailed Template' }
|
|
119
119
|
};
|
|
@@ -129,7 +129,7 @@ describe('WeChat Template Management Sagas', () => {
|
|
|
129
129
|
.run();
|
|
130
130
|
});
|
|
131
131
|
|
|
132
|
-
it
|
|
132
|
+
it('handles failure in fetching template details', () => {
|
|
133
133
|
const error = new Error('Fetch failed');
|
|
134
134
|
|
|
135
135
|
return expectSaga(sagas.getTemplateDetails, id)
|
|
@@ -148,7 +148,7 @@ describe('WeChat Template Management Sagas', () => {
|
|
|
148
148
|
|
|
149
149
|
// Test combined saga
|
|
150
150
|
describe('Combined v2WechatMapTemplatesSagas', () => {
|
|
151
|
-
it
|
|
151
|
+
it('should initialize all WeChat-related watcher sagas without error', () => {
|
|
152
152
|
return expectSaga(v2WechatMapTemplatesSagas)
|
|
153
153
|
.run();
|
|
154
154
|
});
|
|
@@ -45,8 +45,6 @@ import {
|
|
|
45
45
|
ACTION_TYPES,
|
|
46
46
|
NOTIFICATION_TITLE_MAX_LENGTH,
|
|
47
47
|
MESSAGE_MAX_LENGTH,
|
|
48
|
-
EXTERNAL_URL,
|
|
49
|
-
SITE_URL,
|
|
50
48
|
} from '../constants';
|
|
51
49
|
import * as actions from '../actions';
|
|
52
50
|
import {
|
|
@@ -75,9 +73,6 @@ import {
|
|
|
75
73
|
import './index.scss';
|
|
76
74
|
import { WEBPUSH } from '../../CreativesContainer/constants';
|
|
77
75
|
import { isAiContentBotDisabled } from '../../../utils/common';
|
|
78
|
-
import TestAndPreviewSlidebox from '../../../v2Components/TestAndPreviewSlidebox';
|
|
79
|
-
import creativesMessages from '../../CreativesContainer/messages';
|
|
80
|
-
import CapButton from '@capillarytech/cap-ui-library/CapButton';
|
|
81
76
|
|
|
82
77
|
// Memoized TagList wrapper components for better performance
|
|
83
78
|
const MemoizedTagList = memo(({
|
|
@@ -187,7 +182,6 @@ const WebPushCreate = ({
|
|
|
187
182
|
const [redirectUrlError, setRedirectUrlError] = useState('');
|
|
188
183
|
const [activeUploadField, setActiveUploadField] = useState(null);
|
|
189
184
|
const [templateIdError, setTemplateIdError] = useState('');
|
|
190
|
-
const [showTestAndPreviewSlidebox, setShowTestAndPreviewSlidebox] = useState(false);
|
|
191
185
|
|
|
192
186
|
// Refs
|
|
193
187
|
const titleCountRef = useRef(null);
|
|
@@ -575,67 +569,6 @@ const WebPushCreate = ({
|
|
|
575
569
|
return !(templateNameInvalid || titleValidation || messageValidation);
|
|
576
570
|
};
|
|
577
571
|
|
|
578
|
-
const getTemplateContent = useCallback(() => {
|
|
579
|
-
let cta = null;
|
|
580
|
-
if (onClickBehaviour === ON_CLICK_BEHAVIOUR_OPTIONS.REDIRECT_TO_URL && redirectUrl) {
|
|
581
|
-
cta = { type: EXTERNAL_URL, actionLink: redirectUrl.trim() };
|
|
582
|
-
} else if (onClickBehaviour === ON_CLICK_BEHAVIOUR_OPTIONS.SITE_URL && websiteLink) {
|
|
583
|
-
cta = { type: SITE_URL, actionLink: websiteLink };
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
const hasImage = mediaType === WEBPUSH_MEDIA_TYPES.IMAGE && imageSrc;
|
|
587
|
-
const hasCtas = buttons && buttons.length > 0;
|
|
588
|
-
let expandableDetails = null;
|
|
589
|
-
if (hasImage || hasCtas) {
|
|
590
|
-
expandableDetails = {
|
|
591
|
-
media: hasImage ? [{ url: imageSrc, type: WEBPUSH_MEDIA_TYPES.IMAGE }] : [],
|
|
592
|
-
ctas: hasCtas ? buttons.map((btn) => ({
|
|
593
|
-
type: EXTERNAL_URL,
|
|
594
|
-
action: '',
|
|
595
|
-
title: btn.text || '',
|
|
596
|
-
actionLink: btn.url || '',
|
|
597
|
-
})) : [],
|
|
598
|
-
};
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
const iconImageUrl = brandIconOption !== BRAND_ICON_OPTIONS.DONT_SHOW && brandIconSrc ? brandIconSrc : undefined;
|
|
602
|
-
|
|
603
|
-
return {
|
|
604
|
-
channel: WEBPUSH,
|
|
605
|
-
accountId,
|
|
606
|
-
content: {
|
|
607
|
-
title: notificationTitle || '',
|
|
608
|
-
message: message || '',
|
|
609
|
-
...(iconImageUrl ? { iconImageUrl } : {}),
|
|
610
|
-
...(cta ? { cta } : {}),
|
|
611
|
-
...(expandableDetails ? { expandableDetails } : {}),
|
|
612
|
-
},
|
|
613
|
-
messageSubject: templateName || notificationTitle || '',
|
|
614
|
-
offers: [],
|
|
615
|
-
};
|
|
616
|
-
}, [
|
|
617
|
-
notificationTitle,
|
|
618
|
-
message,
|
|
619
|
-
accountId,
|
|
620
|
-
mediaType,
|
|
621
|
-
imageSrc,
|
|
622
|
-
brandIconOption,
|
|
623
|
-
brandIconSrc,
|
|
624
|
-
buttons,
|
|
625
|
-
onClickBehaviour,
|
|
626
|
-
redirectUrl,
|
|
627
|
-
websiteLink,
|
|
628
|
-
templateName,
|
|
629
|
-
]);
|
|
630
|
-
|
|
631
|
-
const handleTestAndPreview = useCallback(() => {
|
|
632
|
-
setShowTestAndPreviewSlidebox(true);
|
|
633
|
-
}, []);
|
|
634
|
-
|
|
635
|
-
const handleCloseTestAndPreview = useCallback(() => {
|
|
636
|
-
setShowTestAndPreviewSlidebox(false);
|
|
637
|
-
}, []);
|
|
638
|
-
|
|
639
572
|
const isFormValid = () => {
|
|
640
573
|
const templateNameInvalid = isFullMode && validateTemplateName(templateName);
|
|
641
574
|
const titleValidation = validateTitle(notificationTitle);
|
|
@@ -1107,23 +1040,14 @@ const WebPushCreate = ({
|
|
|
1107
1040
|
formatMessage={formatMessage}
|
|
1108
1041
|
messages={messages}
|
|
1109
1042
|
/>
|
|
1110
|
-
<
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
/>
|
|
1119
|
-
<CapButton
|
|
1120
|
-
type="secondary"
|
|
1121
|
-
onClick={handleTestAndPreview}
|
|
1122
|
-
className="webpush-test-preview-btn"
|
|
1123
|
-
>
|
|
1124
|
-
{formatMessage(creativesMessages.testAndPreview)}
|
|
1125
|
-
</CapButton>
|
|
1126
|
-
</CapRow>
|
|
1043
|
+
<FormActions
|
|
1044
|
+
onSave={handleSave}
|
|
1045
|
+
isSaveDisabled={isSaveDisabled}
|
|
1046
|
+
errorText={errorText}
|
|
1047
|
+
accountErrorText={accountErrorText}
|
|
1048
|
+
formatMessage={formatMessage}
|
|
1049
|
+
messages={messages}
|
|
1050
|
+
/>
|
|
1127
1051
|
</CapColumn>
|
|
1128
1052
|
<CapColumn className="preview-section" span={10}>
|
|
1129
1053
|
<WebPushPreview
|
|
@@ -1135,13 +1059,6 @@ const WebPushCreate = ({
|
|
|
1135
1059
|
buttons={buttons}
|
|
1136
1060
|
/>
|
|
1137
1061
|
</CapColumn>
|
|
1138
|
-
<TestAndPreviewSlidebox
|
|
1139
|
-
show={showTestAndPreviewSlidebox}
|
|
1140
|
-
onClose={handleCloseTestAndPreview}
|
|
1141
|
-
content={getTemplateContent()}
|
|
1142
|
-
currentChannel={WEBPUSH}
|
|
1143
|
-
accountId={accountId || null}
|
|
1144
|
-
/>
|
|
1145
1062
|
</CapRow>
|
|
1146
1063
|
);
|
|
1147
1064
|
};
|
|
@@ -119,6 +119,7 @@ import { ANDROID } from '../../v2Components/CommonTestAndPreview/constants';
|
|
|
119
119
|
import CapImageUpload from '../../v2Components/CapImageUpload';
|
|
120
120
|
import TagList from '../TagList';
|
|
121
121
|
import { validateTags } from '../../utils/tagValidations';
|
|
122
|
+
import { splitContentByOrderedVarTokens } from '../../utils/templateVarUtils';
|
|
122
123
|
import { capitalizeString } from '../../utils/Formatter';
|
|
123
124
|
import CapWhatsappCTA from '../../v2Components/CapWhatsappCTA';
|
|
124
125
|
import {
|
|
@@ -496,28 +497,10 @@ export const Whatsapp = (props) => {
|
|
|
496
497
|
);
|
|
497
498
|
};
|
|
498
499
|
|
|
499
|
-
const converStringToVarArr = (validVarArr, content) => {
|
|
500
|
-
const templateVarArray = [];
|
|
501
|
-
while (content?.length !== 0) {
|
|
502
|
-
//converting content string to an array split at var
|
|
503
|
-
const index = content.indexOf(validVarArr?.[0]);
|
|
504
|
-
if (index !== -1) {
|
|
505
|
-
templateVarArray.push(content.substring(0, index)); //push string before var
|
|
506
|
-
templateVarArray.push(validVarArr?.[0]); //push var
|
|
507
|
-
content = content.substring(index + validVarArr?.[0]?.length, content?.length); //remaining str
|
|
508
|
-
validVarArr?.shift(); //remove considered var
|
|
509
|
-
} else {
|
|
510
|
-
templateVarArray.push(content); //remaining str
|
|
511
|
-
break;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
return templateVarArray;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
500
|
const computeTextMessage = (msg, varMap, regex) => {
|
|
518
501
|
const validVarArr = msg?.match(regex) || [];
|
|
519
502
|
//conerting msg string to variable arr
|
|
520
|
-
const templateHeaderArray =
|
|
503
|
+
const templateHeaderArray = splitContentByOrderedVarTokens(validVarArr, msg);
|
|
521
504
|
if (templateHeaderArray?.length !== 0) {
|
|
522
505
|
let clonedVarMap = {};
|
|
523
506
|
if (!isEmpty(varMap)) {
|
|
@@ -571,7 +554,7 @@ export const Whatsapp = (props) => {
|
|
|
571
554
|
setUnsubscribeRequired(true);
|
|
572
555
|
}
|
|
573
556
|
//converting msg string to variable arr
|
|
574
|
-
const templateMessageArray =
|
|
557
|
+
const templateMessageArray = splitContentByOrderedVarTokens(validVarArr, msg);
|
|
575
558
|
updateTempMsgArray(templateMessageArray.filter((i) => i === 0 || i));
|
|
576
559
|
};
|
|
577
560
|
|