@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
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import {
|
|
4
|
-
CAP_SPACE_16, CAP_SPACE_32, CAP_SPACE_56, CAP_SPACE_64,
|
|
5
|
-
} from '@capillarytech/cap-ui-library/styled/variables';
|
|
6
|
-
|
|
7
3
|
import CapSlideBox from '@capillarytech/cap-ui-library/CapSlideBox';
|
|
8
4
|
import CapHeader from '@capillarytech/cap-ui-library/CapHeader';
|
|
9
5
|
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
@@ -13,12 +9,11 @@ import CapNotification from '@capillarytech/cap-ui-library/CapNotification';
|
|
|
13
9
|
import { injectIntl, FormattedMessage } from 'react-intl';
|
|
14
10
|
import classnames from 'classnames';
|
|
15
11
|
import {
|
|
16
|
-
isEmpty, get, forEach, cloneDeep, debounce,
|
|
12
|
+
isEmpty, get, forEach, cloneDeep, debounce, pick,
|
|
17
13
|
} from 'lodash';
|
|
18
14
|
import { connect } from 'react-redux';
|
|
19
15
|
import { createStructuredSelector } from 'reselect';
|
|
20
16
|
import { bindActionCreators, compose } from 'redux';
|
|
21
|
-
import styled from 'styled-components';
|
|
22
17
|
import { GA } from '@capillarytech/cap-ui-utils';
|
|
23
18
|
import { DAEMON } from '@capillarytech/vulcan-react-sdk/utils/sagaInjectorTypes';
|
|
24
19
|
|
|
@@ -32,6 +27,7 @@ import SlideBoxContent from './SlideBoxContent';
|
|
|
32
27
|
import * as constants from './constants';
|
|
33
28
|
import * as commonUtil from '../../utils/common';
|
|
34
29
|
import { gtmPush } from '../../utils/gtmTrackers';
|
|
30
|
+
import { normalizeRcsMessageContentForApi } from '../../utils/rcsPayloadUtils';
|
|
35
31
|
import './index.scss';
|
|
36
32
|
import * as templateActions from '../Templates/actions';
|
|
37
33
|
import * as globalActions from '../Cap/actions';
|
|
@@ -47,6 +43,9 @@ import {
|
|
|
47
43
|
import {EXTERNAL_URL, SITE_URL, WEBPUSH_MEDIA_TYPES} from '../WebPush/constants';
|
|
48
44
|
import { IMAGE, VIDEO } from '../Facebook/Advertisement/constant';
|
|
49
45
|
import { RCS_STATUSES } from '../Rcs/constants';
|
|
46
|
+
import { mapRcsCardContentForConsumerWithResolvedTags } from '../Rcs/utils';
|
|
47
|
+
import { pickRcsCardVarMappedEntries } from '../Rcs/rcsLibraryHydrationUtils';
|
|
48
|
+
import { RCS_SMS_FALLBACK_VAR_MAPPED_PROP } from '../../v2Components/CommonTestAndPreview/constants';
|
|
50
49
|
import { CREATIVE } from '../Facebook/constants';
|
|
51
50
|
import { LOYALTY } from '../App/constants';
|
|
52
51
|
import {
|
|
@@ -61,6 +60,11 @@ import { capSagaForFetchSchemaForEntity, capSagaLiquidEntity } from '../Cap/saga
|
|
|
61
60
|
import { v2TemplateSagaWatchGetDefaultBeeTemplates } from '../Templates/sagas';
|
|
62
61
|
import { DYNAMIC_URL } from '../../v2Components/CapWhatsappCTA/constants';
|
|
63
62
|
import ErrorInfoNote from '../../v2Components/ErrorInfoNote';
|
|
63
|
+
import SlideBoxWrapper from './CreativesSlideBoxWrapper';
|
|
64
|
+
import {
|
|
65
|
+
computeLiquidFooterUpdateFromFormBuilder,
|
|
66
|
+
getSlideBoxWrapperMarginFromLiquidErrors,
|
|
67
|
+
} from './embeddedSlideboxUtils';
|
|
64
68
|
|
|
65
69
|
import {
|
|
66
70
|
transformChannelPayload,
|
|
@@ -69,51 +73,24 @@ import {
|
|
|
69
73
|
import { MANUAL_CAROUSEL } from '../MobilePushNew/constants';
|
|
70
74
|
import { BIG_HTML } from '../InApp/constants';
|
|
71
75
|
|
|
72
|
-
/**
|
|
73
|
-
* Returns true if value is "deep empty": no errors present.
|
|
74
|
-
* - null/undefined: empty
|
|
75
|
-
* - string: empty if length === 0
|
|
76
|
-
* - array: empty if length === 0
|
|
77
|
-
* - plain object (e.g. { android: [], ios: [], generic: [] }): empty only if every value is deep-empty
|
|
78
|
-
*/
|
|
79
|
-
function isDeepEmpty(value) {
|
|
80
|
-
if (value == null) return true;
|
|
81
|
-
if (typeof value === 'string') return value.length === 0;
|
|
82
|
-
if (Array.isArray(value)) return value.length === 0;
|
|
83
|
-
if (typeof value === 'object') {
|
|
84
|
-
return Object.values(value).every(isDeepEmpty);
|
|
85
|
-
}
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
76
|
const classPrefix = 'add-creatives-section';
|
|
90
77
|
const CREATIVES_CONTAINER = 'creativesContainer';
|
|
91
78
|
|
|
92
|
-
const SlideBoxWrapper = styled.div`
|
|
93
|
-
.cap-slide-box-v2-container{
|
|
94
|
-
.slidebox-header, .slidebox-content-container{
|
|
95
|
-
margin-bottom: ${({ slideBoxWrapperMargin }) => `${slideBoxWrapperMargin}`};
|
|
96
|
-
padding: 0 rem;
|
|
97
|
-
&.has-footer{
|
|
98
|
-
overflow-x: hidden;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
.slidebox-footer{
|
|
102
|
-
/* Only apply margin-bottom to footer when ErrorInfoNote is shown in footer (BEE editor) */
|
|
103
|
-
/* For HTML Editor, errors are shown in ValidationErrorDisplay (inside content area), so no footer margin needed */
|
|
104
|
-
margin-bottom: ${({ shouldApplyFooterMargin }) => (shouldApplyFooterMargin ? `${CAP_SPACE_16}` : '0')};
|
|
105
|
-
padding: 0 rem;
|
|
106
|
-
&.has-footer{
|
|
107
|
-
overflow-x: hidden;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
`;
|
|
112
79
|
export class Creatives extends React.Component {
|
|
113
80
|
constructor(props) {
|
|
114
81
|
super(props);
|
|
115
82
|
|
|
116
|
-
const
|
|
83
|
+
const useLocalTemplates = get(
|
|
84
|
+
props,
|
|
85
|
+
'localTemplatesConfig.useLocalTemplates',
|
|
86
|
+
get(props, 'useLocalTemplates', false),
|
|
87
|
+
);
|
|
88
|
+
const initialSlidBoxContent = this.getSlideBoxContent({
|
|
89
|
+
mode: props.creativesMode,
|
|
90
|
+
templateData: props.templateData,
|
|
91
|
+
isFullMode: props.isFullMode,
|
|
92
|
+
useLocalTemplates,
|
|
93
|
+
});
|
|
117
94
|
|
|
118
95
|
this.state = {
|
|
119
96
|
isLoadingContent: true,
|
|
@@ -160,7 +137,13 @@ export class Creatives extends React.Component {
|
|
|
160
137
|
}
|
|
161
138
|
|
|
162
139
|
componentWillUnmount() {
|
|
163
|
-
|
|
140
|
+
const isEmbedded = get(this.props, 'location.query.type', '') === "embedded";
|
|
141
|
+
const useLocalTemplates = get(
|
|
142
|
+
this.props,
|
|
143
|
+
'localTemplatesConfig.useLocalTemplates',
|
|
144
|
+
get(this.props, 'useLocalTemplates', false),
|
|
145
|
+
);
|
|
146
|
+
if (isEmbedded && !useLocalTemplates) {
|
|
164
147
|
this.props.templateActions.resetTemplateStoreData();
|
|
165
148
|
}
|
|
166
149
|
this.props.globalActions.clearMetaEntities();
|
|
@@ -762,25 +745,56 @@ export class Creatives extends React.Component {
|
|
|
762
745
|
smsFallBackContent = {},
|
|
763
746
|
creativeName = "",
|
|
764
747
|
channel = constants.RCS,
|
|
765
|
-
|
|
748
|
+
rcsCardVarMapped,
|
|
766
749
|
} = templateData || {};
|
|
767
|
-
const
|
|
750
|
+
const { isFullMode: isFullModeForRcsPayload } = this.props;
|
|
751
|
+
const firstCardIn = (rcsContent.cardContent && rcsContent.cardContent[0]) || {};
|
|
752
|
+
const {
|
|
753
|
+
cardDisplayTitle: _omitDispTitleIn,
|
|
754
|
+
cardDisplayDescription: _omitDispDescIn,
|
|
755
|
+
...cardContent
|
|
756
|
+
} = firstCardIn;
|
|
768
757
|
const Status = RCS_STATUSES.approved || '';
|
|
758
|
+
const mergedCardVarMapped = (() => {
|
|
759
|
+
const nestedCardVarMapped = cardContent?.cardVarMapped;
|
|
760
|
+
const rootMirrorCardVarMapped = rcsCardVarMapped;
|
|
761
|
+
const nestedRecord =
|
|
762
|
+
nestedCardVarMapped != null && typeof nestedCardVarMapped === 'object'
|
|
763
|
+
? nestedCardVarMapped
|
|
764
|
+
: {};
|
|
765
|
+
const rootRecord =
|
|
766
|
+
rootMirrorCardVarMapped != null && typeof rootMirrorCardVarMapped === 'object'
|
|
767
|
+
? rootMirrorCardVarMapped
|
|
768
|
+
: {};
|
|
769
|
+
const mergedFromRootAndNested = {
|
|
770
|
+
...pickRcsCardVarMappedEntries(rootRecord),
|
|
771
|
+
...pickRcsCardVarMappedEntries(nestedRecord),
|
|
772
|
+
};
|
|
773
|
+
return Object.keys(mergedFromRootAndNested).length > 0
|
|
774
|
+
? mergedFromRootAndNested
|
|
775
|
+
: null;
|
|
776
|
+
})();
|
|
777
|
+
// Campaigns (embedded): do not duplicate `cardVarMapped` as root `rcsCardVarMapped` on send —
|
|
778
|
+
// slot map stays on `versions…cardContent[0].cardVarMapped` only. Library full mode keeps root mirror.
|
|
779
|
+
// Use `=== true` so omitted/undefined `isFullMode` does not behave like library (avoids duplicate on approval payload).
|
|
780
|
+
const includeRootRcsCardVarMapped =
|
|
781
|
+
mergedCardVarMapped && isFullModeForRcsPayload === true;
|
|
769
782
|
|
|
770
783
|
creativesTemplateData = {
|
|
771
784
|
type: channel,
|
|
772
785
|
edit: true,
|
|
773
786
|
name: creativeName,
|
|
787
|
+
...(includeRootRcsCardVarMapped ? { rcsCardVarMapped: mergedCardVarMapped } : {}),
|
|
774
788
|
versions: {
|
|
775
789
|
base: {
|
|
776
790
|
content: {
|
|
777
791
|
RCS: {
|
|
778
792
|
rcsContent: {
|
|
779
793
|
...rcsContent,
|
|
780
|
-
...(accountId && !isFullMode && { accountId }),
|
|
781
794
|
cardContent: [
|
|
782
795
|
{
|
|
783
796
|
...cardContent,
|
|
797
|
+
...(mergedCardVarMapped ? { cardVarMapped: mergedCardVarMapped } : {}),
|
|
784
798
|
Status,
|
|
785
799
|
},
|
|
786
800
|
],
|
|
@@ -931,7 +945,10 @@ export class Creatives extends React.Component {
|
|
|
931
945
|
return newExpandableDetails;
|
|
932
946
|
}
|
|
933
947
|
|
|
934
|
-
getCreativesData = async (
|
|
948
|
+
getCreativesData = async (channelParam, template, templateRecords) => { //from creatives to consumers
|
|
949
|
+
const channel = String(
|
|
950
|
+
channelParam || template?.type || get(template, 'value.type') || '',
|
|
951
|
+
).toUpperCase();
|
|
935
952
|
let templateData = { channel };
|
|
936
953
|
switch (channel) {
|
|
937
954
|
case constants.SMS:
|
|
@@ -1225,7 +1242,7 @@ export class Creatives extends React.Component {
|
|
|
1225
1242
|
};
|
|
1226
1243
|
}
|
|
1227
1244
|
break;
|
|
1228
|
-
case constants.FACEBOOK:
|
|
1245
|
+
case constants.FACEBOOK: {
|
|
1229
1246
|
if (template.value) {
|
|
1230
1247
|
const FacebookAd = template?.value?.versions?.base?.content?.FacebookAd;
|
|
1231
1248
|
const { type } = FacebookAd[0];
|
|
@@ -1269,34 +1286,110 @@ export class Creatives extends React.Component {
|
|
|
1269
1286
|
selectedMarketingObjective: template.value.selectedMarketingObjective,
|
|
1270
1287
|
};
|
|
1271
1288
|
}
|
|
1289
|
+
}
|
|
1272
1290
|
break;
|
|
1273
|
-
case constants.RCS:
|
|
1291
|
+
case constants.RCS: {
|
|
1274
1292
|
if (template.value) {
|
|
1275
|
-
const {
|
|
1276
|
-
} = template.value || {};
|
|
1277
|
-
const
|
|
1293
|
+
const { isFullMode: isFullModeForRcsConsumerPayload } = this.props;
|
|
1294
|
+
const { name = "", versions = {} } = template.value || {};
|
|
1295
|
+
const fromSubmit = get(versions, 'base.content.RCS.smsFallBackContent', {});
|
|
1296
|
+
const fromRecords = {
|
|
1297
|
+
...(templateRecords?.smsFallBackContent || {}),
|
|
1298
|
+
...(get(templateRecords, 'versions.base.content.RCS.smsFallBackContent') || {}),
|
|
1299
|
+
};
|
|
1300
|
+
const hasMeaningfulRcsSmsFallback = (smsFallbackPayload) => {
|
|
1301
|
+
if (
|
|
1302
|
+
!smsFallbackPayload
|
|
1303
|
+
|| typeof smsFallbackPayload !== 'object'
|
|
1304
|
+
|| Object.keys(smsFallbackPayload).length === 0
|
|
1305
|
+
) {
|
|
1306
|
+
return false;
|
|
1307
|
+
}
|
|
1308
|
+
const fallbackBodyText = String(
|
|
1309
|
+
smsFallbackPayload.smsContent
|
|
1310
|
+
?? smsFallbackPayload.smsTemplateContent
|
|
1311
|
+
?? smsFallbackPayload.message
|
|
1312
|
+
?? smsFallbackPayload.content
|
|
1313
|
+
?? '',
|
|
1314
|
+
).trim();
|
|
1315
|
+
const fallbackTemplateName = String(
|
|
1316
|
+
smsFallbackPayload.smsTemplateName ?? smsFallbackPayload.templateName ?? '',
|
|
1317
|
+
).trim();
|
|
1318
|
+
const rcsSmsFallbackVarMapped =
|
|
1319
|
+
smsFallbackPayload?.[RCS_SMS_FALLBACK_VAR_MAPPED_PROP];
|
|
1320
|
+
const hasVarMappedEntries =
|
|
1321
|
+
rcsSmsFallbackVarMapped != null
|
|
1322
|
+
&& typeof rcsSmsFallbackVarMapped === 'object'
|
|
1323
|
+
&& Object.keys(rcsSmsFallbackVarMapped).length > 0;
|
|
1324
|
+
return (
|
|
1325
|
+
fallbackBodyText !== ''
|
|
1326
|
+
|| fallbackTemplateName !== ''
|
|
1327
|
+
|| hasVarMappedEntries
|
|
1328
|
+
);
|
|
1329
|
+
};
|
|
1330
|
+
// If submit has only empty strings, do not let it wipe fallback mirrored on templateRecords (library round-trip).
|
|
1331
|
+
const smsFallBackContent = hasMeaningfulRcsSmsFallback(fromSubmit)
|
|
1332
|
+
? { ...fromRecords, ...fromSubmit }
|
|
1333
|
+
: { ...fromSubmit, ...fromRecords };
|
|
1278
1334
|
const {
|
|
1279
|
-
cardContent = [],
|
|
1335
|
+
cardContent: cardContentFromSubmit = [],
|
|
1280
1336
|
contentType = "",
|
|
1281
1337
|
cardType = "",
|
|
1282
1338
|
cardSettings = {},
|
|
1283
|
-
accountId = "",
|
|
1284
1339
|
} = get(versions, 'base.content.RCS.rcsContent', {});
|
|
1340
|
+
const rootRcsCardVarMappedFromSubmit = get(template, 'value.rcsCardVarMapped');
|
|
1341
|
+
const firstCardFromSubmit = Array.isArray(cardContentFromSubmit)
|
|
1342
|
+
? cardContentFromSubmit[0]
|
|
1343
|
+
: null;
|
|
1344
|
+
const cardContent = mapRcsCardContentForConsumerWithResolvedTags(
|
|
1345
|
+
cardContentFromSubmit,
|
|
1346
|
+
rootRcsCardVarMappedFromSubmit,
|
|
1347
|
+
isFullModeForRcsConsumerPayload,
|
|
1348
|
+
);
|
|
1285
1349
|
const rcsContent = {
|
|
1286
1350
|
contentType,
|
|
1287
1351
|
cardType,
|
|
1288
1352
|
cardSettings,
|
|
1289
1353
|
cardContent,
|
|
1290
1354
|
};
|
|
1355
|
+
const cardVarMappedFromFirstRcsCard =
|
|
1356
|
+
firstCardFromSubmit?.cardVarMapped != null
|
|
1357
|
+
&& typeof firstCardFromSubmit.cardVarMapped === 'object'
|
|
1358
|
+
? pickRcsCardVarMappedEntries(firstCardFromSubmit.cardVarMapped)
|
|
1359
|
+
: null;
|
|
1360
|
+
const includeRootRcsCardVarMappedOnConsumerPayload =
|
|
1361
|
+
cardVarMappedFromFirstRcsCard
|
|
1362
|
+
&& Object.keys(cardVarMappedFromFirstRcsCard).length > 0
|
|
1363
|
+
&& isFullModeForRcsConsumerPayload === true;
|
|
1291
1364
|
templateData = {
|
|
1292
1365
|
channel,
|
|
1293
1366
|
creativeName: name,
|
|
1294
1367
|
rcsContent,
|
|
1295
|
-
|
|
1368
|
+
...(includeRootRcsCardVarMappedOnConsumerPayload
|
|
1369
|
+
? { rcsCardVarMapped: cardVarMappedFromFirstRcsCard }
|
|
1370
|
+
: {}),
|
|
1296
1371
|
};
|
|
1372
|
+
// Library / campaign consumers round-trip templateData via getTemplateData; include SMS fallback
|
|
1373
|
+
// so reopening the editor restores fallback text and tag mappings.
|
|
1374
|
+
// cap-campaigns-v2 API expects `smsFallBackContent.message` (see normalizeRcsMessageContentForApi).
|
|
1375
|
+
if (hasMeaningfulRcsSmsFallback(smsFallBackContent)) {
|
|
1376
|
+
const smsText =
|
|
1377
|
+
smsFallBackContent.message
|
|
1378
|
+
?? smsFallBackContent.smsContent
|
|
1379
|
+
?? smsFallBackContent.smsTemplateContent
|
|
1380
|
+
?? '';
|
|
1381
|
+
templateData.smsFallBackContent = {
|
|
1382
|
+
...smsFallBackContent,
|
|
1383
|
+
...(String(smsText).trim() !== ''
|
|
1384
|
+
? { message: String(smsText).trim() }
|
|
1385
|
+
: {}),
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1388
|
+
normalizeRcsMessageContentForApi(templateData);
|
|
1297
1389
|
}
|
|
1390
|
+
}
|
|
1298
1391
|
break;
|
|
1299
|
-
case constants.ZALO:
|
|
1392
|
+
case constants.ZALO: {
|
|
1300
1393
|
if (template.value) {
|
|
1301
1394
|
templateData = {
|
|
1302
1395
|
...template.value,
|
|
@@ -1305,6 +1398,7 @@ export class Creatives extends React.Component {
|
|
|
1305
1398
|
delete templateData.type;
|
|
1306
1399
|
}
|
|
1307
1400
|
}
|
|
1401
|
+
}
|
|
1308
1402
|
break;
|
|
1309
1403
|
case constants.WEBPUSH: {
|
|
1310
1404
|
if (template.value) {
|
|
@@ -1411,7 +1505,10 @@ export class Creatives extends React.Component {
|
|
|
1411
1505
|
return templateData;
|
|
1412
1506
|
};
|
|
1413
1507
|
|
|
1414
|
-
getSlideBoxContent({ mode, templateData, isFullMode }) {
|
|
1508
|
+
getSlideBoxContent({ mode, templateData, isFullMode, useLocalTemplates }) {
|
|
1509
|
+
if (useLocalTemplates && mode === 'create' && isEmpty(templateData)) {
|
|
1510
|
+
return 'templates';
|
|
1511
|
+
}
|
|
1415
1512
|
let creativesMode = isFullMode ? 'createTemplate' : 'templates';// for library mode templates page is initial mode and for full mode createTemplates
|
|
1416
1513
|
if (mode === 'create' && isFullMode) {
|
|
1417
1514
|
creativesMode = 'createTemplate';
|
|
@@ -1499,24 +1596,110 @@ export class Creatives extends React.Component {
|
|
|
1499
1596
|
getFormData = (template) => {
|
|
1500
1597
|
// Always reset isGetFormData so the child does not re-send form data on every re-render
|
|
1501
1598
|
// (e.g. when user fixes validation error by typing, we must not auto-close the slidebox)
|
|
1502
|
-
this.setState(
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
{
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1599
|
+
this.setState(
|
|
1600
|
+
(prevState) => {
|
|
1601
|
+
const next = { isGetFormData: false };
|
|
1602
|
+
if (!template.validity) {
|
|
1603
|
+
return next;
|
|
1604
|
+
}
|
|
1605
|
+
const baseTd = prevState.templateData || template;
|
|
1606
|
+
const channel = (
|
|
1607
|
+
baseTd?.type
|
|
1608
|
+
|| template?.type
|
|
1609
|
+
|| get(template, 'value.type')
|
|
1610
|
+
|| ''
|
|
1611
|
+
).toUpperCase();
|
|
1612
|
+
// Library mode: persist last submitted creatives shape so reopening still hydrates the editor
|
|
1613
|
+
// (parent may not merge getCreativesData back into templateData).
|
|
1614
|
+
if (this.props.isFullMode === false && template.value) {
|
|
1615
|
+
if (channel === constants.RCS) {
|
|
1616
|
+
const smsFallBackFromPayload = get(
|
|
1617
|
+
template.value,
|
|
1618
|
+
'versions.base.content.RCS.smsFallBackContent',
|
|
1619
|
+
);
|
|
1620
|
+
const rcsCardVarMappedFromPayload = get(
|
|
1621
|
+
template.value,
|
|
1622
|
+
'versions.base.content.RCS.rcsContent.cardContent[0].cardVarMapped',
|
|
1623
|
+
);
|
|
1624
|
+
next.templateData = {
|
|
1625
|
+
...baseTd,
|
|
1626
|
+
type: constants.RCS,
|
|
1627
|
+
name: template?.value?.name,
|
|
1628
|
+
versions: template?.value?.versions,
|
|
1629
|
+
...(smsFallBackFromPayload != null
|
|
1630
|
+
&& typeof smsFallBackFromPayload === 'object'
|
|
1631
|
+
&& Object.keys(smsFallBackFromPayload).length > 0
|
|
1632
|
+
? { smsFallBackContent: smsFallBackFromPayload }
|
|
1633
|
+
: {}),
|
|
1634
|
+
...(rcsCardVarMappedFromPayload != null
|
|
1635
|
+
&& typeof rcsCardVarMappedFromPayload === 'object'
|
|
1636
|
+
? { rcsCardVarMapped: rcsCardVarMappedFromPayload }
|
|
1637
|
+
: {}),
|
|
1638
|
+
};
|
|
1639
|
+
if (template._id) {
|
|
1640
|
+
next.templateData._id = template._id;
|
|
1641
|
+
}
|
|
1642
|
+
} else if (channel === constants.SMS) {
|
|
1643
|
+
const submittedSmsTemplateValue = template?.value;
|
|
1644
|
+
const smsVersions =
|
|
1645
|
+
submittedSmsTemplateValue?.history != null
|
|
1646
|
+
? submittedSmsTemplateValue
|
|
1647
|
+
: {
|
|
1648
|
+
base: submittedSmsTemplateValue?.base,
|
|
1649
|
+
history: submittedSmsTemplateValue?.base
|
|
1650
|
+
? [submittedSmsTemplateValue.base]
|
|
1651
|
+
: [],
|
|
1652
|
+
};
|
|
1653
|
+
next.templateData = {
|
|
1654
|
+
...baseTd,
|
|
1655
|
+
type: constants.SMS,
|
|
1656
|
+
name: baseTd?.name || 'Campaign message SMS content',
|
|
1657
|
+
versions: smsVersions,
|
|
1658
|
+
};
|
|
1659
|
+
if (template?._id) {
|
|
1660
|
+
next.templateData._id = template._id;
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
return next;
|
|
1665
|
+
},
|
|
1666
|
+
() => {
|
|
1667
|
+
if (!template.validity) {
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
const templateData = this.state.templateData ? this.state.templateData : template; //select existing or create new content
|
|
1671
|
+
const channelForConsumer = String(
|
|
1672
|
+
templateData.type
|
|
1673
|
+
|| template.type
|
|
1674
|
+
|| get(template, 'value.type')
|
|
1675
|
+
|| '',
|
|
1676
|
+
).toUpperCase();
|
|
1677
|
+
const creativesData = this.getCreativesData(
|
|
1678
|
+
channelForConsumer,
|
|
1679
|
+
template,
|
|
1680
|
+
this.state.templateData || template,
|
|
1681
|
+
);// convers data to consumer understandable format
|
|
1682
|
+
creativesData.then((data) => {
|
|
1683
|
+
this.logGTMEvent(channelForConsumer, data);
|
|
1684
|
+
this.processCentralCommsMetaId(channelForConsumer, data, {
|
|
1685
|
+
closeSlideBoxAfterSubmit: template.closeSlideBoxAfterSubmit,
|
|
1513
1686
|
});
|
|
1514
|
-
}
|
|
1515
|
-
|
|
1516
|
-
|
|
1687
|
+
});
|
|
1688
|
+
},
|
|
1689
|
+
);
|
|
1517
1690
|
}
|
|
1518
1691
|
|
|
1519
|
-
processCentralCommsMetaId = (channel, creativesData) => {
|
|
1692
|
+
processCentralCommsMetaId = (channel, creativesData, options = {}) => {
|
|
1693
|
+
const { closeSlideBoxAfterSubmit = false } = options;
|
|
1694
|
+
const maybeCloseLibrarySlideBox = () => {
|
|
1695
|
+
if (
|
|
1696
|
+
closeSlideBoxAfterSubmit
|
|
1697
|
+
&& this.props.isFullMode === false
|
|
1698
|
+
&& typeof this.handleCloseSlideBox === 'function'
|
|
1699
|
+
) {
|
|
1700
|
+
this.handleCloseSlideBox();
|
|
1701
|
+
}
|
|
1702
|
+
};
|
|
1520
1703
|
// Create the payload for the centralcommnsmetaId API call
|
|
1521
1704
|
const { isLoyaltyModule = false, loyaltyMetaData = {} } = this.props;
|
|
1522
1705
|
const { actionName, setMetaData = () => { } } = loyaltyMetaData;
|
|
@@ -1542,6 +1725,7 @@ export class Creatives extends React.Component {
|
|
|
1542
1725
|
if (result?.status?.code === 200) {
|
|
1543
1726
|
setMetaData(result);
|
|
1544
1727
|
this.props.getCreativesData(creativesData);
|
|
1728
|
+
maybeCloseLibrarySlideBox();
|
|
1545
1729
|
} else {
|
|
1546
1730
|
CapNotification.error({ message: <FormattedMessage {...messages.somethingWentWrong} /> });
|
|
1547
1731
|
}
|
|
@@ -1552,6 +1736,7 @@ export class Creatives extends React.Component {
|
|
|
1552
1736
|
} else {
|
|
1553
1737
|
// If not a loyalty module or different action, should work as usual
|
|
1554
1738
|
this.props.getCreativesData(creativesData);
|
|
1739
|
+
maybeCloseLibrarySlideBox();
|
|
1555
1740
|
}
|
|
1556
1741
|
};
|
|
1557
1742
|
|
|
@@ -1584,7 +1769,9 @@ export class Creatives extends React.Component {
|
|
|
1584
1769
|
}
|
|
1585
1770
|
this.setState((prevState) => ({
|
|
1586
1771
|
...prevState,
|
|
1587
|
-
|
|
1772
|
+
// Library mode (isFullMode === false): retain last template so reopening still has RCS payload.
|
|
1773
|
+
// Undefined isFullMode defaults to full-mode close behavior (clear templateData).
|
|
1774
|
+
...(this.props.isFullMode !== false ? { templateData: undefined } : {}),
|
|
1588
1775
|
showSlideBox: false,
|
|
1589
1776
|
liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
|
|
1590
1777
|
isLiquidValidationError: false,
|
|
@@ -1795,21 +1982,12 @@ export class Creatives extends React.Component {
|
|
|
1795
1982
|
}
|
|
1796
1983
|
|
|
1797
1984
|
showLiquidErrorInFooter = (errorMessagesFromFormBuilder, currentFormBuilderTab) => {
|
|
1798
|
-
const
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
const hasStandard = !isDeepEmpty(standardMsgs);
|
|
1802
|
-
const isLiquidValidationError = hasLiquid || hasStandard;
|
|
1803
|
-
// Don't overwrite existing liquid error with empty only for Mobile Push OLD (FormBuilder/clear calls empty there); SMS/others clear on input change
|
|
1804
|
-
const isMobilePush = this.state.currentChannel?.toUpperCase() === constants.MOBILE_PUSH;
|
|
1805
|
-
if (!hasLiquid && !hasStandard && this.state.isLiquidValidationError && isMobilePush) {
|
|
1806
|
-
return;
|
|
1807
|
-
}
|
|
1808
|
-
this.setState({
|
|
1809
|
-
isLiquidValidationError,
|
|
1810
|
-
liquidErrorMessage: errorMessagesFromFormBuilder,
|
|
1811
|
-
activeFormBuilderTab: currentFormBuilderTab === 1 ? constants.ANDROID : (currentFormBuilderTab === 2 ? constants.IOS : null), // Update activeFormBuilderTab, default to 1 if undefined
|
|
1985
|
+
const next = computeLiquidFooterUpdateFromFormBuilder(errorMessagesFromFormBuilder, currentFormBuilderTab, {
|
|
1986
|
+
previousIsLiquidValidationError: this.state.isLiquidValidationError,
|
|
1987
|
+
currentChannelUpper: this.state.currentChannel?.toUpperCase(),
|
|
1812
1988
|
});
|
|
1989
|
+
if (next == null) return;
|
|
1990
|
+
this.setState(next);
|
|
1813
1991
|
}
|
|
1814
1992
|
|
|
1815
1993
|
// Callback to update HTML Editor validation state (called from EmailWrapper)
|
|
@@ -1932,6 +2110,11 @@ export class Creatives extends React.Component {
|
|
|
1932
2110
|
inAppEditorType,
|
|
1933
2111
|
htmlEditorValidationState,
|
|
1934
2112
|
} = this.state;
|
|
2113
|
+
const useLocalTemplates = get(
|
|
2114
|
+
this.props,
|
|
2115
|
+
'localTemplatesConfig.useLocalTemplates',
|
|
2116
|
+
get(this.props, 'useLocalTemplates', false),
|
|
2117
|
+
);
|
|
1935
2118
|
const {
|
|
1936
2119
|
isFullMode,
|
|
1937
2120
|
creativesMode,
|
|
@@ -1950,7 +2133,6 @@ export class Creatives extends React.Component {
|
|
|
1950
2133
|
smsRegister,
|
|
1951
2134
|
enableNewChannels,
|
|
1952
2135
|
eventContextTags,
|
|
1953
|
-
waitEventContextTags = {},
|
|
1954
2136
|
isLoyaltyModule,
|
|
1955
2137
|
loyaltyMetaData = {},
|
|
1956
2138
|
} = this.props;
|
|
@@ -1984,14 +2166,7 @@ export class Creatives extends React.Component {
|
|
|
1984
2166
|
// IMPORTANT: Never show ErrorInfoNote in footer when in HTML Editor mode, even if liquidErrorMessage exists
|
|
1985
2167
|
const shouldShowErrorInfoNoteInFooter = isHTMLEditorMode ? false : hasBEEEditorErrors;
|
|
1986
2168
|
|
|
1987
|
-
|
|
1988
|
-
const slideBoxWrapperMargin = (get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0 && get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0)
|
|
1989
|
-
? CAP_SPACE_64
|
|
1990
|
-
: get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0
|
|
1991
|
-
? CAP_SPACE_56
|
|
1992
|
-
: get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0
|
|
1993
|
-
? CAP_SPACE_32
|
|
1994
|
-
: 0;
|
|
2169
|
+
const slideBoxWrapperMargin = getSlideBoxWrapperMarginFromLiquidErrors(liquidErrorMessage);
|
|
1995
2170
|
/* TODO: Instead of passing down same props separately to each component down, write common function to these props and pass it accordingly */
|
|
1996
2171
|
|
|
1997
2172
|
// Compute anonymous user type and channel restrictions
|
|
@@ -2020,7 +2195,10 @@ export class Creatives extends React.Component {
|
|
|
2020
2195
|
<SlideBoxWrapper
|
|
2021
2196
|
slideBoxWrapperMargin={slideBoxWrapperMargin}
|
|
2022
2197
|
shouldApplyFooterMargin={shouldShowErrorInfoNoteInFooter}
|
|
2023
|
-
className={classnames(
|
|
2198
|
+
className={classnames(
|
|
2199
|
+
`${classPrefix} ${isFullMode ? 'creatives-full-mode' : 'creatives-library-mode'} ${mapTemplateCreate ? 'map-template-create' : ''}`,
|
|
2200
|
+
useLocalTemplates && slidBoxContent === 'templates' && 'creatives-slidebox--local-sms-templates',
|
|
2201
|
+
)}
|
|
2024
2202
|
>
|
|
2025
2203
|
<CapSlideBox
|
|
2026
2204
|
header={
|
|
@@ -2045,12 +2223,13 @@ export class Creatives extends React.Component {
|
|
|
2045
2223
|
smsRegister={smsRegister}
|
|
2046
2224
|
handleClose={this.handleCloseSlideBox}
|
|
2047
2225
|
moduleType={this.props.messageDetails?.type}
|
|
2226
|
+
useLocalTemplates={useLocalTemplates}
|
|
2048
2227
|
/>
|
|
2049
2228
|
)}
|
|
2050
2229
|
content={(
|
|
2051
2230
|
<SlideBoxContent
|
|
2052
2231
|
key="creatives-container-slidebox-content"
|
|
2053
|
-
onSelectTemplate={this.onSelectTemplate}
|
|
2232
|
+
onSelectTemplate={this.props.onSelectTemplate != null ? this.props.onSelectTemplate : this.onSelectTemplate}
|
|
2054
2233
|
onCreateComplete={getCreativesData}
|
|
2055
2234
|
onPreviewTemplate={this.onPreviewTemplate}
|
|
2056
2235
|
slidBoxContent={slidBoxContent}
|
|
@@ -2118,7 +2297,6 @@ export class Creatives extends React.Component {
|
|
|
2118
2297
|
creativesMode={creativesMode} // An existing prop that we're using here. Required to ensure correct account details in Edit or Preview in case of Embedded mode.
|
|
2119
2298
|
hostName={this.props?.hostName || ''}
|
|
2120
2299
|
eventContextTags={eventContextTags}
|
|
2121
|
-
waitEventContextTags={waitEventContextTags}
|
|
2122
2300
|
isLoyaltyModule={isLoyaltyModule}
|
|
2123
2301
|
loyaltyMetaData={loyaltyMetaData}
|
|
2124
2302
|
showTestAndPreviewSlidebox={showTestAndPreviewSlidebox}
|
|
@@ -2127,7 +2305,8 @@ export class Creatives extends React.Component {
|
|
|
2127
2305
|
isTestAndPreviewMode={(() => this.state.isTestAndPreviewMode)()}
|
|
2128
2306
|
onHtmlEditorValidationStateChange={this.updateHtmlEditorValidationState}
|
|
2129
2307
|
onPersonalizationTokensChange={this.handlePersonalizationTokensChange}
|
|
2130
|
-
|
|
2308
|
+
localTemplatesConfig={pick(this.props.localTemplatesConfig || this.props, constants.LOCAL_TEMPLATE_CONFIG_KEYS)}
|
|
2309
|
+
/>
|
|
2131
2310
|
)}
|
|
2132
2311
|
footer={this.shouldShowFooter() ? (
|
|
2133
2312
|
<SlideBoxFooter
|
|
@@ -2211,13 +2390,31 @@ Creatives.propTypes = {
|
|
|
2211
2390
|
orgUnitId: PropTypes.number,
|
|
2212
2391
|
hostName: PropTypes.string,
|
|
2213
2392
|
eventContextTags: PropTypes.array,
|
|
2214
|
-
waitEventContextTags: PropTypes.object,
|
|
2215
2393
|
loyaltyTagFetchingDependencies: PropTypes.object,
|
|
2216
2394
|
customerType: PropTypes.string,
|
|
2217
2395
|
intl: PropTypes.shape({
|
|
2218
2396
|
formatMessage: PropTypes.func,
|
|
2219
2397
|
}),
|
|
2220
2398
|
stopValidation: PropTypes.func,
|
|
2399
|
+
// Local template list (e.g. for SMS fallback): when set, TemplatesV2 uses these instead of Redux.
|
|
2400
|
+
// All optional. Pass either localTemplatesConfig (object) or individual props below.
|
|
2401
|
+
localTemplatesConfig: PropTypes.shape({
|
|
2402
|
+
useLocalTemplates: PropTypes.bool,
|
|
2403
|
+
localTemplates: PropTypes.arrayOf(PropTypes.object),
|
|
2404
|
+
localTemplatesLoading: PropTypes.bool,
|
|
2405
|
+
localTemplatesFilterContent: PropTypes.node,
|
|
2406
|
+
localTemplatesSentinelContent: PropTypes.node,
|
|
2407
|
+
localTemplatesScrollContainerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
|
|
2408
|
+
localTemplatesUseSkeleton: PropTypes.bool,
|
|
2409
|
+
}),
|
|
2410
|
+
useLocalTemplates: PropTypes.bool,
|
|
2411
|
+
localTemplates: PropTypes.arrayOf(PropTypes.object),
|
|
2412
|
+
localTemplatesLoading: PropTypes.bool,
|
|
2413
|
+
localTemplatesFilterContent: PropTypes.node,
|
|
2414
|
+
localTemplatesSentinelContent: PropTypes.node,
|
|
2415
|
+
localTemplatesScrollContainerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
|
|
2416
|
+
localTemplatesUseSkeleton: PropTypes.bool,
|
|
2417
|
+
onSelectTemplate: PropTypes.func,
|
|
2221
2418
|
};
|
|
2222
2419
|
const mapStatesToProps = () => createStructuredSelector({
|
|
2223
2420
|
isLoading: isLoadingSelector(),
|