@capillarytech/creatives-library 9.0.15-alpha.0 → 9.0.15-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/constants/unified.js +29 -0
- package/global-styles.js +1 -1
- 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 +168 -109
- package/v2Components/CapActionButton/index.scss +156 -5
- package/v2Components/CapActionButton/messages.js +19 -3
- package/v2Components/CapActionButton/tests/index.test.js +42 -18
- package/v2Components/CapImageUpload/index.js +2 -2
- package/v2Components/CapTagList/index.js +10 -0
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -48
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +214 -21
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +83 -9
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +60 -11
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +201 -23
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +355 -76
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +155 -8
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
- package/v2Components/CommonTestAndPreview/constants.js +38 -2
- package/v2Components/CommonTestAndPreview/index.js +761 -222
- package/v2Components/CommonTestAndPreview/messages.js +45 -3
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
- package/v2Components/CommonTestAndPreview/sagas.js +25 -6
- package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +1 -0
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +284 -267
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +231 -65
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
- package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +34 -13
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
- package/v2Components/CommonTestAndPreview/tests/index.test.js +133 -4
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +31 -24
- package/v2Components/CommonTestAndPreview/utils.js +84 -0
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +91 -0
- package/v2Components/SmsFallback/constants.js +94 -0
- package/v2Components/SmsFallback/index.js +958 -0
- package/v2Components/SmsFallback/index.scss +266 -0
- package/v2Components/SmsFallback/messages.js +78 -0
- package/v2Components/SmsFallback/smsFallbackUtils.js +120 -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 +208 -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 +41 -22
- package/v2Components/TemplatePreview/constants.js +2 -0
- package/v2Components/TemplatePreview/index.js +143 -31
- package/v2Components/TemplatePreview/tests/index.test.js +142 -0
- package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +6 -6
- 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/CommunicationFlow/Tests/CommunicationFlow.test.js +4 -1
- package/v2Containers/CommunicationFlow/index.js +3 -3
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +4 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +20 -20
- package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +17 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +36 -4
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +14 -5
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +36 -5
- package/v2Containers/CreativesContainer/constants.js +11 -0
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +79 -0
- package/v2Containers/CreativesContainer/index.js +323 -104
- package/v2Containers/CreativesContainer/index.scss +83 -1
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +79 -34
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +79 -16
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +333 -90
- 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 +131 -11
- package/v2Containers/Rcs/index.js +2602 -807
- package/v2Containers/Rcs/index.scss +289 -8
- package/v2Containers/Rcs/messages.js +34 -3
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +227 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +73535 -38537
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -8
- package/v2Containers/Rcs/tests/index.test.js +147 -36
- 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 +115 -48
- 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 +678 -169
- package/v2Containers/SmsTrai/Edit/index.scss +126 -0
- package/v2Containers/SmsTrai/Edit/messages.js +14 -4
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +5615 -3014
- 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 +171 -12
- package/v2Containers/Templates/actions.js +11 -0
- package/v2Containers/Templates/constants.js +2 -0
- package/v2Containers/Templates/index.js +125 -55
- package/v2Containers/Templates/sagas.js +57 -13
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1060 -1015
- package/v2Containers/Templates/tests/sagas.test.js +199 -16
- package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
- package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
- package/v2Containers/TemplatesV2/index.js +86 -23
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
- package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
- package/v2Containers/Whatsapp/index.js +3 -20
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
|
@@ -44,10 +44,6 @@ class TemplateNameInputField extends React.Component {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
import PropTypes from 'prop-types';
|
|
47
|
-
import {
|
|
48
|
-
CAP_SPACE_16, CAP_SPACE_32, CAP_SPACE_56, CAP_SPACE_64,
|
|
49
|
-
} from '@capillarytech/cap-ui-library/styled/variables';
|
|
50
|
-
|
|
51
47
|
import CapSlideBox from '@capillarytech/cap-ui-library/CapSlideBox';
|
|
52
48
|
import CapHeader from '@capillarytech/cap-ui-library/CapHeader';
|
|
53
49
|
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
@@ -57,12 +53,11 @@ import CapNotification from '@capillarytech/cap-ui-library/CapNotification';
|
|
|
57
53
|
import { injectIntl, FormattedMessage } from 'react-intl';
|
|
58
54
|
import classnames from 'classnames';
|
|
59
55
|
import {
|
|
60
|
-
isEmpty, get, forEach, cloneDeep, debounce,
|
|
56
|
+
isEmpty, get, forEach, cloneDeep, debounce, pick,
|
|
61
57
|
} from 'lodash';
|
|
62
58
|
import { connect } from 'react-redux';
|
|
63
59
|
import { createStructuredSelector } from 'reselect';
|
|
64
60
|
import { bindActionCreators, compose } from 'redux';
|
|
65
|
-
import styled from 'styled-components';
|
|
66
61
|
import { GA } from '@capillarytech/cap-ui-utils';
|
|
67
62
|
import { DAEMON } from '@capillarytech/vulcan-react-sdk/utils/sagaInjectorTypes';
|
|
68
63
|
|
|
@@ -76,6 +71,7 @@ import SlideBoxContent from './SlideBoxContent';
|
|
|
76
71
|
import * as constants from './constants';
|
|
77
72
|
import * as commonUtil from '../../utils/common';
|
|
78
73
|
import { gtmPush } from '../../utils/gtmTrackers';
|
|
74
|
+
import { normalizeRcsMessageContentForApi } from '../../utils/rcsPayloadUtils';
|
|
79
75
|
import './index.scss';
|
|
80
76
|
import * as templateActions from '../Templates/actions';
|
|
81
77
|
import * as globalActions from '../Cap/actions';
|
|
@@ -90,7 +86,10 @@ import {
|
|
|
90
86
|
} from '../Line/Container/constants';
|
|
91
87
|
import {EXTERNAL_URL, SITE_URL, WEBPUSH_MEDIA_TYPES} from '../WebPush/constants';
|
|
92
88
|
import { IMAGE, VIDEO } from '../Facebook/Advertisement/constant';
|
|
93
|
-
import { RCS_STATUSES } from '../Rcs/constants';
|
|
89
|
+
import { RCS_STATUSES, contentType as RCS_CONTENT_TYPE, EMBEDDED } from '../Rcs/constants';
|
|
90
|
+
import { mapRcsCardContentForConsumerWithResolvedTags } from '../Rcs/utils';
|
|
91
|
+
import { pickRcsCardVarMappedEntries } from '../Rcs/rcsLibraryHydrationUtils';
|
|
92
|
+
import { RCS_SMS_FALLBACK_VAR_MAPPED_PROP } from '../../v2Components/CommonTestAndPreview/constants';
|
|
94
93
|
import { CREATIVE } from '../Facebook/constants';
|
|
95
94
|
import { LOYALTY } from '../App/constants';
|
|
96
95
|
import {
|
|
@@ -131,6 +130,11 @@ import { capSagaForFetchSchemaForEntity, capSagaLiquidEntity } from '../Cap/saga
|
|
|
131
130
|
import { v2TemplateSagaWatchGetDefaultBeeTemplates } from '../Templates/sagas';
|
|
132
131
|
import { DYNAMIC_URL } from '../../v2Components/CapWhatsappCTA/constants';
|
|
133
132
|
import ErrorInfoNote from '../../v2Components/ErrorInfoNote';
|
|
133
|
+
import SlideBoxWrapper from './CreativesSlideBoxWrapper';
|
|
134
|
+
import {
|
|
135
|
+
computeLiquidFooterUpdateFromFormBuilder,
|
|
136
|
+
getSlideBoxWrapperMarginFromLiquidErrors,
|
|
137
|
+
} from './embeddedSlideboxUtils';
|
|
134
138
|
|
|
135
139
|
import {
|
|
136
140
|
transformChannelPayload,
|
|
@@ -139,51 +143,24 @@ import {
|
|
|
139
143
|
import { MANUAL_CAROUSEL } from '../MobilePushNew/constants';
|
|
140
144
|
import { BIG_HTML } from '../InApp/constants';
|
|
141
145
|
|
|
142
|
-
/**
|
|
143
|
-
* Returns true if value is "deep empty": no errors present.
|
|
144
|
-
* - null/undefined: empty
|
|
145
|
-
* - string: empty if length === 0
|
|
146
|
-
* - array: empty if length === 0
|
|
147
|
-
* - plain object (e.g. { android: [], ios: [], generic: [] }): empty only if every value is deep-empty
|
|
148
|
-
*/
|
|
149
|
-
function isDeepEmpty(value) {
|
|
150
|
-
if (value == null) return true;
|
|
151
|
-
if (typeof value === 'string') return value.length === 0;
|
|
152
|
-
if (Array.isArray(value)) return value.length === 0;
|
|
153
|
-
if (typeof value === 'object') {
|
|
154
|
-
return Object.values(value).every(isDeepEmpty);
|
|
155
|
-
}
|
|
156
|
-
return false;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
146
|
const classPrefix = 'add-creatives-section';
|
|
160
147
|
const CREATIVES_CONTAINER = 'creativesContainer';
|
|
161
148
|
|
|
162
|
-
const SlideBoxWrapper = styled.div`
|
|
163
|
-
.cap-slide-box-v2-container{
|
|
164
|
-
.slidebox-header, .slidebox-content-container{
|
|
165
|
-
margin-bottom: ${({ slideBoxWrapperMargin }) => `${slideBoxWrapperMargin}`};
|
|
166
|
-
padding: 0 rem;
|
|
167
|
-
&.has-footer{
|
|
168
|
-
overflow-x: hidden;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
.slidebox-footer{
|
|
172
|
-
/* Only apply margin-bottom to footer when ErrorInfoNote is shown in footer (BEE editor) */
|
|
173
|
-
/* For HTML Editor, errors are shown in ValidationErrorDisplay (inside content area), so no footer margin needed */
|
|
174
|
-
margin-bottom: ${({ shouldApplyFooterMargin }) => (shouldApplyFooterMargin ? `${CAP_SPACE_16}` : '0')};
|
|
175
|
-
padding: 0 rem;
|
|
176
|
-
&.has-footer{
|
|
177
|
-
overflow-x: hidden;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
`;
|
|
182
149
|
export class Creatives extends React.Component {
|
|
183
150
|
constructor(props) {
|
|
184
151
|
super(props);
|
|
185
152
|
|
|
186
|
-
const
|
|
153
|
+
const useLocalTemplates = get(
|
|
154
|
+
props,
|
|
155
|
+
'localTemplatesConfig.useLocalTemplates',
|
|
156
|
+
get(props, 'useLocalTemplates', false),
|
|
157
|
+
);
|
|
158
|
+
const initialSlidBoxContent = this.getSlideBoxContent({
|
|
159
|
+
mode: props.creativesMode,
|
|
160
|
+
templateData: props.templateData,
|
|
161
|
+
isFullMode: props.isFullMode,
|
|
162
|
+
useLocalTemplates,
|
|
163
|
+
});
|
|
187
164
|
|
|
188
165
|
this.state = {
|
|
189
166
|
isLoadingContent: true,
|
|
@@ -230,7 +207,13 @@ export class Creatives extends React.Component {
|
|
|
230
207
|
}
|
|
231
208
|
|
|
232
209
|
componentWillUnmount() {
|
|
233
|
-
|
|
210
|
+
const isEmbedded = get(this.props, 'location.query.type', '') === EMBEDDED;
|
|
211
|
+
const useLocalTemplates = get(
|
|
212
|
+
this.props,
|
|
213
|
+
'localTemplatesConfig.useLocalTemplates',
|
|
214
|
+
get(this.props, 'useLocalTemplates', false),
|
|
215
|
+
);
|
|
216
|
+
if (isEmbedded && !useLocalTemplates) {
|
|
234
217
|
this.props.templateActions.resetTemplateStoreData();
|
|
235
218
|
}
|
|
236
219
|
this.props.globalActions.clearMetaEntities();
|
|
@@ -840,15 +823,69 @@ export class Creatives extends React.Component {
|
|
|
840
823
|
smsFallBackContent = {},
|
|
841
824
|
creativeName = "",
|
|
842
825
|
channel = constants.RCS,
|
|
826
|
+
rcsCardVarMapped,
|
|
843
827
|
accountId = "",
|
|
844
828
|
} = templateData || {};
|
|
845
|
-
const
|
|
829
|
+
const { isFullMode: isFullModeForRcsPayload } = this.props;
|
|
830
|
+
const isCarouselRcs = (rcsContent?.cardType || '').toString().toUpperCase() === RCS_CONTENT_TYPE.carousel;
|
|
831
|
+
const firstCardIn = (rcsContent.cardContent && rcsContent.cardContent[0]) || {};
|
|
832
|
+
const {
|
|
833
|
+
cardDisplayTitle: _omitDispTitleIn,
|
|
834
|
+
cardDisplayDescription: _omitDispDescIn,
|
|
835
|
+
...cardContent
|
|
836
|
+
} = firstCardIn;
|
|
846
837
|
const Status = RCS_STATUSES.approved || '';
|
|
838
|
+
const mergedCardVarMapped = (() => {
|
|
839
|
+
const nestedCardVarMapped = cardContent?.cardVarMapped;
|
|
840
|
+
const rootMirrorCardVarMapped = rcsCardVarMapped;
|
|
841
|
+
const nestedRecord =
|
|
842
|
+
nestedCardVarMapped != null && typeof nestedCardVarMapped === 'object'
|
|
843
|
+
? nestedCardVarMapped
|
|
844
|
+
: {};
|
|
845
|
+
const rootRecord =
|
|
846
|
+
rootMirrorCardVarMapped != null && typeof rootMirrorCardVarMapped === 'object'
|
|
847
|
+
? rootMirrorCardVarMapped
|
|
848
|
+
: {};
|
|
849
|
+
const mergedFromRootAndNested = {
|
|
850
|
+
...pickRcsCardVarMappedEntries(rootRecord),
|
|
851
|
+
...pickRcsCardVarMappedEntries(nestedRecord),
|
|
852
|
+
};
|
|
853
|
+
return Object.keys(mergedFromRootAndNested).length > 0
|
|
854
|
+
? mergedFromRootAndNested
|
|
855
|
+
: null;
|
|
856
|
+
})();
|
|
857
|
+
// Campaigns (embedded): do not duplicate `cardVarMapped` as root `rcsCardVarMapped` on send —
|
|
858
|
+
// slot map stays on `versions…cardContent[0].cardVarMapped` only. Library full mode keeps root mirror.
|
|
859
|
+
// Use `=== true` so omitted/undefined `isFullMode` does not behave like library (avoids duplicate on approval payload).
|
|
860
|
+
const includeRootRcsCardVarMapped =
|
|
861
|
+
mergedCardVarMapped && isFullModeForRcsPayload === true;
|
|
862
|
+
|
|
863
|
+
const builtCardContent = isCarouselRcs
|
|
864
|
+
? (rcsContent.cardContent || []).map((card, idx) => {
|
|
865
|
+
const {
|
|
866
|
+
cardDisplayTitle: _dt,
|
|
867
|
+
cardDisplayDescription: _dd,
|
|
868
|
+
...restCard
|
|
869
|
+
} = card || {};
|
|
870
|
+
return {
|
|
871
|
+
...restCard,
|
|
872
|
+
...(idx === 0 && mergedCardVarMapped ? { cardVarMapped: mergedCardVarMapped } : {}),
|
|
873
|
+
Status,
|
|
874
|
+
};
|
|
875
|
+
})
|
|
876
|
+
: [
|
|
877
|
+
{
|
|
878
|
+
...cardContent,
|
|
879
|
+
...(mergedCardVarMapped ? { cardVarMapped: mergedCardVarMapped } : {}),
|
|
880
|
+
Status,
|
|
881
|
+
},
|
|
882
|
+
];
|
|
847
883
|
|
|
848
884
|
creativesTemplateData = {
|
|
849
885
|
type: channel,
|
|
850
886
|
edit: true,
|
|
851
887
|
name: creativeName,
|
|
888
|
+
...(includeRootRcsCardVarMapped ? { rcsCardVarMapped: mergedCardVarMapped } : {}),
|
|
852
889
|
versions: {
|
|
853
890
|
base: {
|
|
854
891
|
content: {
|
|
@@ -856,12 +893,7 @@ export class Creatives extends React.Component {
|
|
|
856
893
|
rcsContent: {
|
|
857
894
|
...rcsContent,
|
|
858
895
|
...(accountId && !isFullMode && { accountId }),
|
|
859
|
-
cardContent:
|
|
860
|
-
{
|
|
861
|
-
...cardContent,
|
|
862
|
-
Status,
|
|
863
|
-
},
|
|
864
|
-
],
|
|
896
|
+
cardContent: builtCardContent,
|
|
865
897
|
},
|
|
866
898
|
smsFallBackContent,
|
|
867
899
|
},
|
|
@@ -1009,7 +1041,10 @@ export class Creatives extends React.Component {
|
|
|
1009
1041
|
return newExpandableDetails;
|
|
1010
1042
|
}
|
|
1011
1043
|
|
|
1012
|
-
getCreativesData = async (
|
|
1044
|
+
getCreativesData = async (channelParam, template, templateRecords) => { //from creatives to consumers
|
|
1045
|
+
const channel = String(
|
|
1046
|
+
channelParam || template?.type || get(template, 'value.type') || '',
|
|
1047
|
+
).toUpperCase();
|
|
1013
1048
|
let templateData = { channel };
|
|
1014
1049
|
switch (channel) {
|
|
1015
1050
|
case constants.SMS:
|
|
@@ -1356,28 +1391,104 @@ export class Creatives extends React.Component {
|
|
|
1356
1391
|
break;
|
|
1357
1392
|
case constants.RCS:
|
|
1358
1393
|
if (template.value) {
|
|
1359
|
-
const {
|
|
1360
|
-
} = template.value || {};
|
|
1361
|
-
const
|
|
1394
|
+
const { isFullMode: isFullModeForRcsConsumerPayload } = this.props;
|
|
1395
|
+
const { name = "", versions = {} } = template.value || {};
|
|
1396
|
+
const fromSubmit = get(versions, 'base.content.RCS.smsFallBackContent', {});
|
|
1397
|
+
const fromRecords = {
|
|
1398
|
+
...(templateRecords?.smsFallBackContent || {}),
|
|
1399
|
+
...(get(templateRecords, 'versions.base.content.RCS.smsFallBackContent') || {}),
|
|
1400
|
+
};
|
|
1401
|
+
const hasMeaningfulRcsSmsFallback = (smsFallbackPayload) => {
|
|
1402
|
+
if (
|
|
1403
|
+
!smsFallbackPayload
|
|
1404
|
+
|| typeof smsFallbackPayload !== 'object'
|
|
1405
|
+
|| Object.keys(smsFallbackPayload).length === 0
|
|
1406
|
+
) {
|
|
1407
|
+
return false;
|
|
1408
|
+
}
|
|
1409
|
+
const fallbackBodyText = String(
|
|
1410
|
+
smsFallbackPayload.smsContent
|
|
1411
|
+
?? smsFallbackPayload.smsTemplateContent
|
|
1412
|
+
?? smsFallbackPayload.message
|
|
1413
|
+
?? smsFallbackPayload.content
|
|
1414
|
+
?? '',
|
|
1415
|
+
).trim();
|
|
1416
|
+
const fallbackTemplateName = String(
|
|
1417
|
+
smsFallbackPayload.smsTemplateName ?? smsFallbackPayload.templateName ?? '',
|
|
1418
|
+
).trim();
|
|
1419
|
+
const rcsSmsFallbackVarMapped =
|
|
1420
|
+
smsFallbackPayload?.[RCS_SMS_FALLBACK_VAR_MAPPED_PROP];
|
|
1421
|
+
const hasVarMappedEntries =
|
|
1422
|
+
rcsSmsFallbackVarMapped != null
|
|
1423
|
+
&& typeof rcsSmsFallbackVarMapped === 'object'
|
|
1424
|
+
&& Object.keys(rcsSmsFallbackVarMapped).length > 0;
|
|
1425
|
+
return (
|
|
1426
|
+
fallbackBodyText !== ''
|
|
1427
|
+
|| fallbackTemplateName !== ''
|
|
1428
|
+
|| hasVarMappedEntries
|
|
1429
|
+
);
|
|
1430
|
+
};
|
|
1431
|
+
// If submit has only empty strings, do not let it wipe fallback mirrored on templateRecords (library round-trip).
|
|
1432
|
+
const smsFallBackContent = hasMeaningfulRcsSmsFallback(fromSubmit)
|
|
1433
|
+
? { ...fromRecords, ...fromSubmit }
|
|
1434
|
+
: { ...fromSubmit, ...fromRecords };
|
|
1362
1435
|
const {
|
|
1363
|
-
cardContent = [],
|
|
1436
|
+
cardContent: cardContentFromSubmit = [],
|
|
1364
1437
|
contentType = "",
|
|
1365
1438
|
cardType = "",
|
|
1366
1439
|
cardSettings = {},
|
|
1367
1440
|
accountId = "",
|
|
1368
1441
|
} = get(versions, 'base.content.RCS.rcsContent', {});
|
|
1442
|
+
const rootRcsCardVarMappedFromSubmit = get(template, 'value.rcsCardVarMapped');
|
|
1443
|
+
const firstCardFromSubmit = Array.isArray(cardContentFromSubmit)
|
|
1444
|
+
? cardContentFromSubmit[0]
|
|
1445
|
+
: null;
|
|
1446
|
+
const cardContent = mapRcsCardContentForConsumerWithResolvedTags(
|
|
1447
|
+
cardContentFromSubmit,
|
|
1448
|
+
rootRcsCardVarMappedFromSubmit,
|
|
1449
|
+
isFullModeForRcsConsumerPayload,
|
|
1450
|
+
);
|
|
1369
1451
|
const rcsContent = {
|
|
1370
1452
|
contentType,
|
|
1371
1453
|
cardType,
|
|
1372
1454
|
cardSettings,
|
|
1373
1455
|
cardContent,
|
|
1374
1456
|
};
|
|
1457
|
+
const cardVarMappedFromFirstRcsCard =
|
|
1458
|
+
firstCardFromSubmit?.cardVarMapped != null
|
|
1459
|
+
&& typeof firstCardFromSubmit.cardVarMapped === 'object'
|
|
1460
|
+
? pickRcsCardVarMappedEntries(firstCardFromSubmit.cardVarMapped)
|
|
1461
|
+
: null;
|
|
1462
|
+
const includeRootRcsCardVarMappedOnConsumerPayload =
|
|
1463
|
+
cardVarMappedFromFirstRcsCard
|
|
1464
|
+
&& Object.keys(cardVarMappedFromFirstRcsCard).length > 0
|
|
1465
|
+
&& isFullModeForRcsConsumerPayload === true;
|
|
1375
1466
|
templateData = {
|
|
1376
1467
|
channel,
|
|
1377
1468
|
creativeName: name,
|
|
1378
1469
|
rcsContent,
|
|
1379
1470
|
accountId,
|
|
1471
|
+
...(includeRootRcsCardVarMappedOnConsumerPayload
|
|
1472
|
+
? { rcsCardVarMapped: cardVarMappedFromFirstRcsCard }
|
|
1473
|
+
: {}),
|
|
1380
1474
|
};
|
|
1475
|
+
// Library / campaign consumers round-trip templateData via getTemplateData; include SMS fallback
|
|
1476
|
+
// so reopening the editor restores fallback text and tag mappings.
|
|
1477
|
+
// cap-campaigns-v2 API expects `smsFallBackContent.message` (see normalizeRcsMessageContentForApi).
|
|
1478
|
+
if (hasMeaningfulRcsSmsFallback(smsFallBackContent)) {
|
|
1479
|
+
const smsText =
|
|
1480
|
+
smsFallBackContent.message
|
|
1481
|
+
?? smsFallBackContent.smsContent
|
|
1482
|
+
?? smsFallBackContent.smsTemplateContent
|
|
1483
|
+
?? '';
|
|
1484
|
+
templateData.smsFallBackContent = {
|
|
1485
|
+
...smsFallBackContent,
|
|
1486
|
+
...(String(smsText).trim() !== ''
|
|
1487
|
+
? { message: String(smsText).trim() }
|
|
1488
|
+
: {}),
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
normalizeRcsMessageContentForApi(templateData);
|
|
1381
1492
|
}
|
|
1382
1493
|
break;
|
|
1383
1494
|
case constants.ZALO:
|
|
@@ -1495,7 +1606,10 @@ export class Creatives extends React.Component {
|
|
|
1495
1606
|
return templateData;
|
|
1496
1607
|
};
|
|
1497
1608
|
|
|
1498
|
-
getSlideBoxContent({ mode, templateData, isFullMode }) {
|
|
1609
|
+
getSlideBoxContent({ mode, templateData, isFullMode, useLocalTemplates }) {
|
|
1610
|
+
if (useLocalTemplates && mode === 'create' && isEmpty(templateData)) {
|
|
1611
|
+
return 'templates';
|
|
1612
|
+
}
|
|
1499
1613
|
let creativesMode = isFullMode ? 'createTemplate' : 'templates';// for library mode templates page is initial mode and for full mode createTemplates
|
|
1500
1614
|
if (mode === 'create' && isFullMode) {
|
|
1501
1615
|
creativesMode = 'createTemplate';
|
|
@@ -1583,24 +1697,110 @@ export class Creatives extends React.Component {
|
|
|
1583
1697
|
getFormData = (template) => {
|
|
1584
1698
|
// Always reset isGetFormData so the child does not re-send form data on every re-render
|
|
1585
1699
|
// (e.g. when user fixes validation error by typing, we must not auto-close the slidebox)
|
|
1586
|
-
this.setState(
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
{
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1700
|
+
this.setState(
|
|
1701
|
+
(prevState) => {
|
|
1702
|
+
const next = { isGetFormData: false };
|
|
1703
|
+
if (!template.validity) {
|
|
1704
|
+
return next;
|
|
1705
|
+
}
|
|
1706
|
+
const baseTd = prevState.templateData || template;
|
|
1707
|
+
const channel = (
|
|
1708
|
+
baseTd?.type
|
|
1709
|
+
|| template?.type
|
|
1710
|
+
|| get(template, 'value.type')
|
|
1711
|
+
|| ''
|
|
1712
|
+
).toUpperCase();
|
|
1713
|
+
// Library mode: persist last submitted creatives shape so reopening still hydrates the editor
|
|
1714
|
+
// (parent may not merge getCreativesData back into templateData).
|
|
1715
|
+
if (this.props.isFullMode === false && template.value) {
|
|
1716
|
+
if (channel === constants.RCS) {
|
|
1717
|
+
const smsFallBackFromPayload = get(
|
|
1718
|
+
template.value,
|
|
1719
|
+
'versions.base.content.RCS.smsFallBackContent',
|
|
1720
|
+
);
|
|
1721
|
+
const rcsCardVarMappedFromPayload = get(
|
|
1722
|
+
template.value,
|
|
1723
|
+
'versions.base.content.RCS.rcsContent.cardContent[0].cardVarMapped',
|
|
1724
|
+
);
|
|
1725
|
+
next.templateData = {
|
|
1726
|
+
...baseTd,
|
|
1727
|
+
type: constants.RCS,
|
|
1728
|
+
name: template?.value?.name,
|
|
1729
|
+
versions: template?.value?.versions,
|
|
1730
|
+
...(smsFallBackFromPayload != null
|
|
1731
|
+
&& typeof smsFallBackFromPayload === 'object'
|
|
1732
|
+
&& Object.keys(smsFallBackFromPayload).length > 0
|
|
1733
|
+
? { smsFallBackContent: smsFallBackFromPayload }
|
|
1734
|
+
: {}),
|
|
1735
|
+
...(rcsCardVarMappedFromPayload != null
|
|
1736
|
+
&& typeof rcsCardVarMappedFromPayload === 'object'
|
|
1737
|
+
? { rcsCardVarMapped: rcsCardVarMappedFromPayload }
|
|
1738
|
+
: {}),
|
|
1739
|
+
};
|
|
1740
|
+
if (template._id) {
|
|
1741
|
+
next.templateData._id = template._id;
|
|
1742
|
+
}
|
|
1743
|
+
} else if (channel === constants.SMS) {
|
|
1744
|
+
const submittedSmsTemplateValue = template?.value;
|
|
1745
|
+
const smsVersions =
|
|
1746
|
+
submittedSmsTemplateValue?.history != null
|
|
1747
|
+
? submittedSmsTemplateValue
|
|
1748
|
+
: {
|
|
1749
|
+
base: submittedSmsTemplateValue?.base,
|
|
1750
|
+
history: submittedSmsTemplateValue?.base
|
|
1751
|
+
? [submittedSmsTemplateValue.base]
|
|
1752
|
+
: [],
|
|
1753
|
+
};
|
|
1754
|
+
next.templateData = {
|
|
1755
|
+
...baseTd,
|
|
1756
|
+
type: constants.SMS,
|
|
1757
|
+
name: baseTd?.name || constants.DEFAULT_SMS_TEMPLATE_NAME,
|
|
1758
|
+
versions: smsVersions,
|
|
1759
|
+
};
|
|
1760
|
+
if (template?._id) {
|
|
1761
|
+
next.templateData._id = template._id;
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
return next;
|
|
1766
|
+
},
|
|
1767
|
+
() => {
|
|
1768
|
+
if (!template.validity) {
|
|
1769
|
+
return;
|
|
1770
|
+
}
|
|
1771
|
+
const templateData = this.state.templateData ? this.state.templateData : template; //select existing or create new content
|
|
1772
|
+
const channelForConsumer = String(
|
|
1773
|
+
templateData.type
|
|
1774
|
+
|| template.type
|
|
1775
|
+
|| get(template, 'value.type')
|
|
1776
|
+
|| '',
|
|
1777
|
+
).toUpperCase();
|
|
1778
|
+
const creativesData = this.getCreativesData(
|
|
1779
|
+
channelForConsumer,
|
|
1780
|
+
template,
|
|
1781
|
+
this.state.templateData || template,
|
|
1782
|
+
);// convers data to consumer understandable format
|
|
1783
|
+
creativesData.then((data) => {
|
|
1784
|
+
this.logGTMEvent(channelForConsumer, data);
|
|
1785
|
+
this.processCentralCommsMetaId(channelForConsumer, data, {
|
|
1786
|
+
closeSlideBoxAfterSubmit: template.closeSlideBoxAfterSubmit,
|
|
1597
1787
|
});
|
|
1598
|
-
}
|
|
1599
|
-
|
|
1600
|
-
|
|
1788
|
+
});
|
|
1789
|
+
},
|
|
1790
|
+
);
|
|
1601
1791
|
}
|
|
1602
1792
|
|
|
1603
|
-
processCentralCommsMetaId = (channel, creativesData) => {
|
|
1793
|
+
processCentralCommsMetaId = (channel, creativesData, options = {}) => {
|
|
1794
|
+
const { closeSlideBoxAfterSubmit = false } = options;
|
|
1795
|
+
const maybeCloseLibrarySlideBox = () => {
|
|
1796
|
+
if (
|
|
1797
|
+
closeSlideBoxAfterSubmit
|
|
1798
|
+
&& this.props.isFullMode === false
|
|
1799
|
+
&& typeof this.handleCloseSlideBox === 'function'
|
|
1800
|
+
) {
|
|
1801
|
+
this.handleCloseSlideBox();
|
|
1802
|
+
}
|
|
1803
|
+
};
|
|
1604
1804
|
// Create the payload for the centralcommnsmetaId API call
|
|
1605
1805
|
const { isLoyaltyModule = false, loyaltyMetaData = {} } = this.props;
|
|
1606
1806
|
const { actionName, setMetaData = () => { } } = loyaltyMetaData;
|
|
@@ -1626,6 +1826,7 @@ export class Creatives extends React.Component {
|
|
|
1626
1826
|
if (result?.status?.code === 200) {
|
|
1627
1827
|
setMetaData(result);
|
|
1628
1828
|
this.props.getCreativesData(creativesData);
|
|
1829
|
+
maybeCloseLibrarySlideBox();
|
|
1629
1830
|
} else {
|
|
1630
1831
|
CapNotification.error({ message: <FormattedMessage {...messages.somethingWentWrong} /> });
|
|
1631
1832
|
}
|
|
@@ -1636,6 +1837,7 @@ export class Creatives extends React.Component {
|
|
|
1636
1837
|
} else {
|
|
1637
1838
|
// If not a loyalty module or different action, should work as usual
|
|
1638
1839
|
this.props.getCreativesData(creativesData);
|
|
1840
|
+
maybeCloseLibrarySlideBox();
|
|
1639
1841
|
}
|
|
1640
1842
|
};
|
|
1641
1843
|
|
|
@@ -1668,7 +1870,9 @@ export class Creatives extends React.Component {
|
|
|
1668
1870
|
}
|
|
1669
1871
|
this.setState((prevState) => ({
|
|
1670
1872
|
...prevState,
|
|
1671
|
-
|
|
1873
|
+
// Library mode (isFullMode === false): retain last template so reopening still has RCS payload.
|
|
1874
|
+
// Undefined isFullMode defaults to full-mode close behavior (clear templateData).
|
|
1875
|
+
...(this.props.isFullMode !== false ? { templateData: undefined } : {}),
|
|
1672
1876
|
showSlideBox: false,
|
|
1673
1877
|
liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
|
|
1674
1878
|
isLiquidValidationError: false,
|
|
@@ -1871,21 +2075,17 @@ export class Creatives extends React.Component {
|
|
|
1871
2075
|
}
|
|
1872
2076
|
|
|
1873
2077
|
showLiquidErrorInFooter = (errorMessagesFromFormBuilder, currentFormBuilderTab) => {
|
|
1874
|
-
const
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
this.setState(
|
|
1885
|
-
isLiquidValidationError,
|
|
1886
|
-
liquidErrorMessage: errorMessagesFromFormBuilder,
|
|
1887
|
-
activeFormBuilderTab: currentFormBuilderTab === 1 ? constants.ANDROID : (currentFormBuilderTab === 2 ? constants.IOS : null), // Update activeFormBuilderTab, default to 1 if undefined
|
|
1888
|
-
});
|
|
2078
|
+
const next = computeLiquidFooterUpdateFromFormBuilder(
|
|
2079
|
+
errorMessagesFromFormBuilder,
|
|
2080
|
+
this.state.liquidErrorMessage,
|
|
2081
|
+
currentFormBuilderTab,
|
|
2082
|
+
{
|
|
2083
|
+
previousIsLiquidValidationError: this.state.isLiquidValidationError,
|
|
2084
|
+
currentChannelUpper: this.state.currentChannel?.toUpperCase(),
|
|
2085
|
+
},
|
|
2086
|
+
);
|
|
2087
|
+
if (next == null) return;
|
|
2088
|
+
this.setState(next);
|
|
1889
2089
|
}
|
|
1890
2090
|
|
|
1891
2091
|
// Callback to update HTML Editor validation state (called from EmailWrapper)
|
|
@@ -2008,6 +2208,11 @@ export class Creatives extends React.Component {
|
|
|
2008
2208
|
inAppEditorType,
|
|
2009
2209
|
htmlEditorValidationState,
|
|
2010
2210
|
} = this.state;
|
|
2211
|
+
const useLocalTemplates = get(
|
|
2212
|
+
this.props,
|
|
2213
|
+
'localTemplatesConfig.useLocalTemplates',
|
|
2214
|
+
get(this.props, 'useLocalTemplates', false),
|
|
2215
|
+
);
|
|
2011
2216
|
const {
|
|
2012
2217
|
isFullMode,
|
|
2013
2218
|
creativesMode,
|
|
@@ -2026,7 +2231,6 @@ export class Creatives extends React.Component {
|
|
|
2026
2231
|
smsRegister,
|
|
2027
2232
|
enableNewChannels,
|
|
2028
2233
|
eventContextTags,
|
|
2029
|
-
waitEventContextTags = {},
|
|
2030
2234
|
isLoyaltyModule,
|
|
2031
2235
|
loyaltyMetaData = {},
|
|
2032
2236
|
} = this.props;
|
|
@@ -2060,14 +2264,7 @@ export class Creatives extends React.Component {
|
|
|
2060
2264
|
// IMPORTANT: Never show ErrorInfoNote in footer when in HTML Editor mode, even if liquidErrorMessage exists
|
|
2061
2265
|
const shouldShowErrorInfoNoteInFooter = isHTMLEditorMode ? false : hasBEEEditorErrors;
|
|
2062
2266
|
|
|
2063
|
-
|
|
2064
|
-
const slideBoxWrapperMargin = (get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0 && get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0)
|
|
2065
|
-
? CAP_SPACE_64
|
|
2066
|
-
: get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0
|
|
2067
|
-
? CAP_SPACE_56
|
|
2068
|
-
: get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0
|
|
2069
|
-
? CAP_SPACE_32
|
|
2070
|
-
: 0;
|
|
2267
|
+
const slideBoxWrapperMargin = getSlideBoxWrapperMarginFromLiquidErrors(liquidErrorMessage);
|
|
2071
2268
|
/* TODO: Instead of passing down same props separately to each component down, write common function to these props and pass it accordingly */
|
|
2072
2269
|
|
|
2073
2270
|
// Compute anonymous user type and channel restrictions
|
|
@@ -2096,7 +2293,10 @@ export class Creatives extends React.Component {
|
|
|
2096
2293
|
<SlideBoxWrapper
|
|
2097
2294
|
slideBoxWrapperMargin={slideBoxWrapperMargin}
|
|
2098
2295
|
shouldApplyFooterMargin={shouldShowErrorInfoNoteInFooter}
|
|
2099
|
-
className={classnames(
|
|
2296
|
+
className={classnames(
|
|
2297
|
+
`${classPrefix} ${isFullMode ? 'creatives-full-mode' : 'creatives-library-mode'} ${mapTemplateCreate ? 'map-template-create' : ''}`,
|
|
2298
|
+
useLocalTemplates && slidBoxContent === 'templates' && 'creatives-slidebox--local-sms-templates',
|
|
2299
|
+
)}
|
|
2100
2300
|
>
|
|
2101
2301
|
<CapSlideBox
|
|
2102
2302
|
header={
|
|
@@ -2121,12 +2321,13 @@ export class Creatives extends React.Component {
|
|
|
2121
2321
|
smsRegister={smsRegister}
|
|
2122
2322
|
handleClose={this.handleCloseSlideBox}
|
|
2123
2323
|
moduleType={this.props.messageDetails?.type}
|
|
2324
|
+
useLocalTemplates={useLocalTemplates}
|
|
2124
2325
|
/>
|
|
2125
2326
|
)}
|
|
2126
2327
|
content={(
|
|
2127
2328
|
<SlideBoxContent
|
|
2128
2329
|
key="creatives-container-slidebox-content"
|
|
2129
|
-
onSelectTemplate={this.onSelectTemplate}
|
|
2330
|
+
onSelectTemplate={this.props.onSelectTemplate != null ? this.props.onSelectTemplate : this.onSelectTemplate}
|
|
2130
2331
|
onCreateComplete={getCreativesData}
|
|
2131
2332
|
onPreviewTemplate={this.onPreviewTemplate}
|
|
2132
2333
|
slidBoxContent={slidBoxContent}
|
|
@@ -2194,7 +2395,6 @@ export class Creatives extends React.Component {
|
|
|
2194
2395
|
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.
|
|
2195
2396
|
hostName={this.props?.hostName || ''}
|
|
2196
2397
|
eventContextTags={eventContextTags}
|
|
2197
|
-
waitEventContextTags={waitEventContextTags}
|
|
2198
2398
|
isLoyaltyModule={isLoyaltyModule}
|
|
2199
2399
|
loyaltyMetaData={loyaltyMetaData}
|
|
2200
2400
|
showTestAndPreviewSlidebox={showTestAndPreviewSlidebox}
|
|
@@ -2203,7 +2403,8 @@ export class Creatives extends React.Component {
|
|
|
2203
2403
|
isTestAndPreviewMode={(() => this.state.isTestAndPreviewMode)()}
|
|
2204
2404
|
onHtmlEditorValidationStateChange={this.updateHtmlEditorValidationState}
|
|
2205
2405
|
onPersonalizationTokensChange={this.handlePersonalizationTokensChange}
|
|
2206
|
-
|
|
2406
|
+
localTemplatesConfig={pick(this.props.localTemplatesConfig || this.props, constants.LOCAL_TEMPLATE_CONFIG_KEYS)}
|
|
2407
|
+
/>
|
|
2207
2408
|
)}
|
|
2208
2409
|
footer={this.shouldShowFooter() ? (
|
|
2209
2410
|
<SlideBoxFooter
|
|
@@ -2288,13 +2489,31 @@ Creatives.propTypes = {
|
|
|
2288
2489
|
orgUnitId: PropTypes.number,
|
|
2289
2490
|
hostName: PropTypes.string,
|
|
2290
2491
|
eventContextTags: PropTypes.array,
|
|
2291
|
-
waitEventContextTags: PropTypes.object,
|
|
2292
2492
|
loyaltyTagFetchingDependencies: PropTypes.object,
|
|
2293
2493
|
customerType: PropTypes.string,
|
|
2294
2494
|
intl: PropTypes.shape({
|
|
2295
2495
|
formatMessage: PropTypes.func,
|
|
2296
2496
|
}),
|
|
2297
2497
|
stopValidation: PropTypes.func,
|
|
2498
|
+
// Local template list (e.g. for SMS fallback): when set, TemplatesV2 uses these instead of Redux.
|
|
2499
|
+
// All optional. Pass either localTemplatesConfig (object) or individual props below.
|
|
2500
|
+
localTemplatesConfig: PropTypes.shape({
|
|
2501
|
+
useLocalTemplates: PropTypes.bool,
|
|
2502
|
+
localTemplates: PropTypes.arrayOf(PropTypes.object),
|
|
2503
|
+
localTemplatesLoading: PropTypes.bool,
|
|
2504
|
+
localTemplatesFilterContent: PropTypes.node,
|
|
2505
|
+
localTemplatesSentinelContent: PropTypes.node,
|
|
2506
|
+
localTemplatesScrollContainerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
|
|
2507
|
+
localTemplatesUseSkeleton: PropTypes.bool,
|
|
2508
|
+
}),
|
|
2509
|
+
useLocalTemplates: PropTypes.bool,
|
|
2510
|
+
localTemplates: PropTypes.arrayOf(PropTypes.object),
|
|
2511
|
+
localTemplatesLoading: PropTypes.bool,
|
|
2512
|
+
localTemplatesFilterContent: PropTypes.node,
|
|
2513
|
+
localTemplatesSentinelContent: PropTypes.node,
|
|
2514
|
+
localTemplatesScrollContainerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
|
|
2515
|
+
localTemplatesUseSkeleton: PropTypes.bool,
|
|
2516
|
+
onSelectTemplate: PropTypes.func,
|
|
2298
2517
|
};
|
|
2299
2518
|
const mapStatesToProps = () => createStructuredSelector({
|
|
2300
2519
|
isLoading: isLoadingSelector(),
|