@capillarytech/creatives-library 8.0.353-alpha.4 → 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/RcsPreviewContent.js +157 -15
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +346 -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 +691 -186
- package/v2Components/CommonTestAndPreview/messages.js +45 -3
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
- package/v2Components/CommonTestAndPreview/sagas.js +25 -6
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +308 -284
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +231 -65
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
- package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +34 -13
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
- package/v2Components/CommonTestAndPreview/tests/index.test.js +132 -4
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +36 -26
- package/v2Components/FormBuilder/index.js +74 -166
- 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 +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 +79 -0
- package/v2Containers/CreativesContainer/index.js +346 -163
- 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/index.js +1 -1
- 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 +120 -52
- package/v2Containers/Templates/sagas.js +56 -12
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1062 -1017
- package/v2Containers/Templates/tests/sagas.test.js +199 -16
- package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
- package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
- package/v2Containers/TemplatesV2/index.js +86 -23
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
- package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
- package/v2Containers/Whatsapp/index.js +3 -20
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
|
@@ -1,53 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
3
|
-
// Isolated input for the email template name field.
|
|
4
|
-
// Manages its own value in local state so keystrokes only re-render this
|
|
5
|
-
// small component, not the entire CreativesContainer → Email → FormBuilder tree.
|
|
6
|
-
class TemplateNameInputField extends React.Component {
|
|
7
|
-
constructor(props) {
|
|
8
|
-
super(props);
|
|
9
|
-
this.state = { localValue: props.initialValue || '' };
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
componentDidUpdate(prevProps) {
|
|
13
|
-
// Sync from props only when the external value changed AND the user hasn't
|
|
14
|
-
// diverged from the previous prop value. This handles async data-load in edit
|
|
15
|
-
// mode without overwriting what the user is actively typing.
|
|
16
|
-
if (
|
|
17
|
-
prevProps.initialValue !== this.props.initialValue &&
|
|
18
|
-
this.state.localValue === (prevProps.initialValue || '')
|
|
19
|
-
) {
|
|
20
|
-
this.setState({ localValue: this.props.initialValue || '' });
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
handleChange = (ev) => {
|
|
25
|
-
const { value } = ev.currentTarget;
|
|
26
|
-
this.setState({ localValue: value });
|
|
27
|
-
if (this.props.onChange) this.props.onChange(value);
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
handleBlur = () => {
|
|
31
|
-
if (this.props.onBlur) this.props.onBlur(this.state.localValue);
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
render() {
|
|
35
|
-
const { onChange: _onChange, initialValue: _initialValue, onBlur: _ob, ...rest } = this.props;
|
|
36
|
-
return (
|
|
37
|
-
<CapInput
|
|
38
|
-
{...rest}
|
|
39
|
-
value={this.state.localValue}
|
|
40
|
-
onChange={this.handleChange}
|
|
41
|
-
onBlur={this.handleBlur}
|
|
42
|
-
/>
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
2
|
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
3
|
import CapSlideBox from '@capillarytech/cap-ui-library/CapSlideBox';
|
|
52
4
|
import CapHeader from '@capillarytech/cap-ui-library/CapHeader';
|
|
53
5
|
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
@@ -57,12 +9,11 @@ import CapNotification from '@capillarytech/cap-ui-library/CapNotification';
|
|
|
57
9
|
import { injectIntl, FormattedMessage } from 'react-intl';
|
|
58
10
|
import classnames from 'classnames';
|
|
59
11
|
import {
|
|
60
|
-
isEmpty, get, forEach, cloneDeep, debounce,
|
|
12
|
+
isEmpty, get, forEach, cloneDeep, debounce, pick,
|
|
61
13
|
} from 'lodash';
|
|
62
14
|
import { connect } from 'react-redux';
|
|
63
15
|
import { createStructuredSelector } from 'reselect';
|
|
64
16
|
import { bindActionCreators, compose } from 'redux';
|
|
65
|
-
import styled from 'styled-components';
|
|
66
17
|
import { GA } from '@capillarytech/cap-ui-utils';
|
|
67
18
|
import { DAEMON } from '@capillarytech/vulcan-react-sdk/utils/sagaInjectorTypes';
|
|
68
19
|
|
|
@@ -76,6 +27,7 @@ import SlideBoxContent from './SlideBoxContent';
|
|
|
76
27
|
import * as constants from './constants';
|
|
77
28
|
import * as commonUtil from '../../utils/common';
|
|
78
29
|
import { gtmPush } from '../../utils/gtmTrackers';
|
|
30
|
+
import { normalizeRcsMessageContentForApi } from '../../utils/rcsPayloadUtils';
|
|
79
31
|
import './index.scss';
|
|
80
32
|
import * as templateActions from '../Templates/actions';
|
|
81
33
|
import * as globalActions from '../Cap/actions';
|
|
@@ -91,6 +43,9 @@ import {
|
|
|
91
43
|
import {EXTERNAL_URL, SITE_URL, WEBPUSH_MEDIA_TYPES} from '../WebPush/constants';
|
|
92
44
|
import { IMAGE, VIDEO } from '../Facebook/Advertisement/constant';
|
|
93
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';
|
|
94
49
|
import { CREATIVE } from '../Facebook/constants';
|
|
95
50
|
import { LOYALTY } from '../App/constants';
|
|
96
51
|
import {
|
|
@@ -105,6 +60,11 @@ import { capSagaForFetchSchemaForEntity, capSagaLiquidEntity } from '../Cap/saga
|
|
|
105
60
|
import { v2TemplateSagaWatchGetDefaultBeeTemplates } from '../Templates/sagas';
|
|
106
61
|
import { DYNAMIC_URL } from '../../v2Components/CapWhatsappCTA/constants';
|
|
107
62
|
import ErrorInfoNote from '../../v2Components/ErrorInfoNote';
|
|
63
|
+
import SlideBoxWrapper from './CreativesSlideBoxWrapper';
|
|
64
|
+
import {
|
|
65
|
+
computeLiquidFooterUpdateFromFormBuilder,
|
|
66
|
+
getSlideBoxWrapperMarginFromLiquidErrors,
|
|
67
|
+
} from './embeddedSlideboxUtils';
|
|
108
68
|
|
|
109
69
|
import {
|
|
110
70
|
transformChannelPayload,
|
|
@@ -113,51 +73,24 @@ import {
|
|
|
113
73
|
import { MANUAL_CAROUSEL } from '../MobilePushNew/constants';
|
|
114
74
|
import { BIG_HTML } from '../InApp/constants';
|
|
115
75
|
|
|
116
|
-
/**
|
|
117
|
-
* Returns true if value is "deep empty": no errors present.
|
|
118
|
-
* - null/undefined: empty
|
|
119
|
-
* - string: empty if length === 0
|
|
120
|
-
* - array: empty if length === 0
|
|
121
|
-
* - plain object (e.g. { android: [], ios: [], generic: [] }): empty only if every value is deep-empty
|
|
122
|
-
*/
|
|
123
|
-
function isDeepEmpty(value) {
|
|
124
|
-
if (value == null) return true;
|
|
125
|
-
if (typeof value === 'string') return value.length === 0;
|
|
126
|
-
if (Array.isArray(value)) return value.length === 0;
|
|
127
|
-
if (typeof value === 'object') {
|
|
128
|
-
return Object.values(value).every(isDeepEmpty);
|
|
129
|
-
}
|
|
130
|
-
return false;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
76
|
const classPrefix = 'add-creatives-section';
|
|
134
77
|
const CREATIVES_CONTAINER = 'creativesContainer';
|
|
135
78
|
|
|
136
|
-
const SlideBoxWrapper = styled.div`
|
|
137
|
-
.cap-slide-box-v2-container{
|
|
138
|
-
.slidebox-header, .slidebox-content-container{
|
|
139
|
-
margin-bottom: ${({ slideBoxWrapperMargin }) => `${slideBoxWrapperMargin}`};
|
|
140
|
-
padding: 0 rem;
|
|
141
|
-
&.has-footer{
|
|
142
|
-
overflow-x: hidden;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
.slidebox-footer{
|
|
146
|
-
/* Only apply margin-bottom to footer when ErrorInfoNote is shown in footer (BEE editor) */
|
|
147
|
-
/* For HTML Editor, errors are shown in ValidationErrorDisplay (inside content area), so no footer margin needed */
|
|
148
|
-
margin-bottom: ${({ shouldApplyFooterMargin }) => (shouldApplyFooterMargin ? `${CAP_SPACE_16}` : '0')};
|
|
149
|
-
padding: 0 rem;
|
|
150
|
-
&.has-footer{
|
|
151
|
-
overflow-x: hidden;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
`;
|
|
156
79
|
export class Creatives extends React.Component {
|
|
157
80
|
constructor(props) {
|
|
158
81
|
super(props);
|
|
159
82
|
|
|
160
|
-
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
|
+
});
|
|
161
94
|
|
|
162
95
|
this.state = {
|
|
163
96
|
isLoadingContent: true,
|
|
@@ -204,7 +137,13 @@ export class Creatives extends React.Component {
|
|
|
204
137
|
}
|
|
205
138
|
|
|
206
139
|
componentWillUnmount() {
|
|
207
|
-
|
|
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) {
|
|
208
147
|
this.props.templateActions.resetTemplateStoreData();
|
|
209
148
|
}
|
|
210
149
|
this.props.globalActions.clearMetaEntities();
|
|
@@ -812,15 +751,69 @@ export class Creatives extends React.Component {
|
|
|
812
751
|
smsFallBackContent = {},
|
|
813
752
|
creativeName = "",
|
|
814
753
|
channel = constants.RCS,
|
|
754
|
+
rcsCardVarMapped,
|
|
815
755
|
accountId = "",
|
|
816
756
|
} = templateData || {};
|
|
817
|
-
const
|
|
757
|
+
const { isFullMode: isFullModeForRcsPayload } = this.props;
|
|
758
|
+
const isCarouselRcs = (rcsContent?.cardType || '').toString().toLowerCase() === 'carousel';
|
|
759
|
+
const firstCardIn = (rcsContent.cardContent && rcsContent.cardContent[0]) || {};
|
|
760
|
+
const {
|
|
761
|
+
cardDisplayTitle: _omitDispTitleIn,
|
|
762
|
+
cardDisplayDescription: _omitDispDescIn,
|
|
763
|
+
...cardContent
|
|
764
|
+
} = firstCardIn;
|
|
818
765
|
const Status = RCS_STATUSES.approved || '';
|
|
766
|
+
const mergedCardVarMapped = (() => {
|
|
767
|
+
const nestedCardVarMapped = cardContent?.cardVarMapped;
|
|
768
|
+
const rootMirrorCardVarMapped = rcsCardVarMapped;
|
|
769
|
+
const nestedRecord =
|
|
770
|
+
nestedCardVarMapped != null && typeof nestedCardVarMapped === 'object'
|
|
771
|
+
? nestedCardVarMapped
|
|
772
|
+
: {};
|
|
773
|
+
const rootRecord =
|
|
774
|
+
rootMirrorCardVarMapped != null && typeof rootMirrorCardVarMapped === 'object'
|
|
775
|
+
? rootMirrorCardVarMapped
|
|
776
|
+
: {};
|
|
777
|
+
const mergedFromRootAndNested = {
|
|
778
|
+
...pickRcsCardVarMappedEntries(rootRecord),
|
|
779
|
+
...pickRcsCardVarMappedEntries(nestedRecord),
|
|
780
|
+
};
|
|
781
|
+
return Object.keys(mergedFromRootAndNested).length > 0
|
|
782
|
+
? mergedFromRootAndNested
|
|
783
|
+
: null;
|
|
784
|
+
})();
|
|
785
|
+
// Campaigns (embedded): do not duplicate `cardVarMapped` as root `rcsCardVarMapped` on send —
|
|
786
|
+
// slot map stays on `versions…cardContent[0].cardVarMapped` only. Library full mode keeps root mirror.
|
|
787
|
+
// Use `=== true` so omitted/undefined `isFullMode` does not behave like library (avoids duplicate on approval payload).
|
|
788
|
+
const includeRootRcsCardVarMapped =
|
|
789
|
+
mergedCardVarMapped && isFullModeForRcsPayload === true;
|
|
790
|
+
|
|
791
|
+
const builtCardContent = isCarouselRcs
|
|
792
|
+
? (rcsContent.cardContent || []).map((card, idx) => {
|
|
793
|
+
const {
|
|
794
|
+
cardDisplayTitle: _dt,
|
|
795
|
+
cardDisplayDescription: _dd,
|
|
796
|
+
...restCard
|
|
797
|
+
} = card || {};
|
|
798
|
+
return {
|
|
799
|
+
...restCard,
|
|
800
|
+
...(idx === 0 && mergedCardVarMapped ? { cardVarMapped: mergedCardVarMapped } : {}),
|
|
801
|
+
Status,
|
|
802
|
+
};
|
|
803
|
+
})
|
|
804
|
+
: [
|
|
805
|
+
{
|
|
806
|
+
...cardContent,
|
|
807
|
+
...(mergedCardVarMapped ? { cardVarMapped: mergedCardVarMapped } : {}),
|
|
808
|
+
Status,
|
|
809
|
+
},
|
|
810
|
+
];
|
|
819
811
|
|
|
820
812
|
creativesTemplateData = {
|
|
821
813
|
type: channel,
|
|
822
814
|
edit: true,
|
|
823
815
|
name: creativeName,
|
|
816
|
+
...(includeRootRcsCardVarMapped ? { rcsCardVarMapped: mergedCardVarMapped } : {}),
|
|
824
817
|
versions: {
|
|
825
818
|
base: {
|
|
826
819
|
content: {
|
|
@@ -828,12 +821,7 @@ export class Creatives extends React.Component {
|
|
|
828
821
|
rcsContent: {
|
|
829
822
|
...rcsContent,
|
|
830
823
|
...(accountId && !isFullMode && { accountId }),
|
|
831
|
-
cardContent:
|
|
832
|
-
{
|
|
833
|
-
...cardContent,
|
|
834
|
-
Status,
|
|
835
|
-
},
|
|
836
|
-
],
|
|
824
|
+
cardContent: builtCardContent,
|
|
837
825
|
},
|
|
838
826
|
smsFallBackContent,
|
|
839
827
|
},
|
|
@@ -981,7 +969,10 @@ export class Creatives extends React.Component {
|
|
|
981
969
|
return newExpandableDetails;
|
|
982
970
|
}
|
|
983
971
|
|
|
984
|
-
getCreativesData = async (
|
|
972
|
+
getCreativesData = async (channelParam, template, templateRecords) => { //from creatives to consumers
|
|
973
|
+
const channel = String(
|
|
974
|
+
channelParam || template?.type || get(template, 'value.type') || '',
|
|
975
|
+
).toUpperCase();
|
|
985
976
|
let templateData = { channel };
|
|
986
977
|
switch (channel) {
|
|
987
978
|
case constants.SMS:
|
|
@@ -1328,28 +1319,104 @@ export class Creatives extends React.Component {
|
|
|
1328
1319
|
break;
|
|
1329
1320
|
case constants.RCS:
|
|
1330
1321
|
if (template.value) {
|
|
1331
|
-
const {
|
|
1332
|
-
} = template.value || {};
|
|
1333
|
-
const
|
|
1322
|
+
const { isFullMode: isFullModeForRcsConsumerPayload } = this.props;
|
|
1323
|
+
const { name = "", versions = {} } = template.value || {};
|
|
1324
|
+
const fromSubmit = get(versions, 'base.content.RCS.smsFallBackContent', {});
|
|
1325
|
+
const fromRecords = {
|
|
1326
|
+
...(templateRecords?.smsFallBackContent || {}),
|
|
1327
|
+
...(get(templateRecords, 'versions.base.content.RCS.smsFallBackContent') || {}),
|
|
1328
|
+
};
|
|
1329
|
+
const hasMeaningfulRcsSmsFallback = (smsFallbackPayload) => {
|
|
1330
|
+
if (
|
|
1331
|
+
!smsFallbackPayload
|
|
1332
|
+
|| typeof smsFallbackPayload !== 'object'
|
|
1333
|
+
|| Object.keys(smsFallbackPayload).length === 0
|
|
1334
|
+
) {
|
|
1335
|
+
return false;
|
|
1336
|
+
}
|
|
1337
|
+
const fallbackBodyText = String(
|
|
1338
|
+
smsFallbackPayload.smsContent
|
|
1339
|
+
?? smsFallbackPayload.smsTemplateContent
|
|
1340
|
+
?? smsFallbackPayload.message
|
|
1341
|
+
?? smsFallbackPayload.content
|
|
1342
|
+
?? '',
|
|
1343
|
+
).trim();
|
|
1344
|
+
const fallbackTemplateName = String(
|
|
1345
|
+
smsFallbackPayload.smsTemplateName ?? smsFallbackPayload.templateName ?? '',
|
|
1346
|
+
).trim();
|
|
1347
|
+
const rcsSmsFallbackVarMapped =
|
|
1348
|
+
smsFallbackPayload?.[RCS_SMS_FALLBACK_VAR_MAPPED_PROP];
|
|
1349
|
+
const hasVarMappedEntries =
|
|
1350
|
+
rcsSmsFallbackVarMapped != null
|
|
1351
|
+
&& typeof rcsSmsFallbackVarMapped === 'object'
|
|
1352
|
+
&& Object.keys(rcsSmsFallbackVarMapped).length > 0;
|
|
1353
|
+
return (
|
|
1354
|
+
fallbackBodyText !== ''
|
|
1355
|
+
|| fallbackTemplateName !== ''
|
|
1356
|
+
|| hasVarMappedEntries
|
|
1357
|
+
);
|
|
1358
|
+
};
|
|
1359
|
+
// If submit has only empty strings, do not let it wipe fallback mirrored on templateRecords (library round-trip).
|
|
1360
|
+
const smsFallBackContent = hasMeaningfulRcsSmsFallback(fromSubmit)
|
|
1361
|
+
? { ...fromRecords, ...fromSubmit }
|
|
1362
|
+
: { ...fromSubmit, ...fromRecords };
|
|
1334
1363
|
const {
|
|
1335
|
-
cardContent = [],
|
|
1364
|
+
cardContent: cardContentFromSubmit = [],
|
|
1336
1365
|
contentType = "",
|
|
1337
1366
|
cardType = "",
|
|
1338
1367
|
cardSettings = {},
|
|
1339
1368
|
accountId = "",
|
|
1340
1369
|
} = get(versions, 'base.content.RCS.rcsContent', {});
|
|
1370
|
+
const rootRcsCardVarMappedFromSubmit = get(template, 'value.rcsCardVarMapped');
|
|
1371
|
+
const firstCardFromSubmit = Array.isArray(cardContentFromSubmit)
|
|
1372
|
+
? cardContentFromSubmit[0]
|
|
1373
|
+
: null;
|
|
1374
|
+
const cardContent = mapRcsCardContentForConsumerWithResolvedTags(
|
|
1375
|
+
cardContentFromSubmit,
|
|
1376
|
+
rootRcsCardVarMappedFromSubmit,
|
|
1377
|
+
isFullModeForRcsConsumerPayload,
|
|
1378
|
+
);
|
|
1341
1379
|
const rcsContent = {
|
|
1342
1380
|
contentType,
|
|
1343
1381
|
cardType,
|
|
1344
1382
|
cardSettings,
|
|
1345
1383
|
cardContent,
|
|
1346
1384
|
};
|
|
1385
|
+
const cardVarMappedFromFirstRcsCard =
|
|
1386
|
+
firstCardFromSubmit?.cardVarMapped != null
|
|
1387
|
+
&& typeof firstCardFromSubmit.cardVarMapped === 'object'
|
|
1388
|
+
? pickRcsCardVarMappedEntries(firstCardFromSubmit.cardVarMapped)
|
|
1389
|
+
: null;
|
|
1390
|
+
const includeRootRcsCardVarMappedOnConsumerPayload =
|
|
1391
|
+
cardVarMappedFromFirstRcsCard
|
|
1392
|
+
&& Object.keys(cardVarMappedFromFirstRcsCard).length > 0
|
|
1393
|
+
&& isFullModeForRcsConsumerPayload === true;
|
|
1347
1394
|
templateData = {
|
|
1348
1395
|
channel,
|
|
1349
1396
|
creativeName: name,
|
|
1350
1397
|
rcsContent,
|
|
1351
1398
|
accountId,
|
|
1399
|
+
...(includeRootRcsCardVarMappedOnConsumerPayload
|
|
1400
|
+
? { rcsCardVarMapped: cardVarMappedFromFirstRcsCard }
|
|
1401
|
+
: {}),
|
|
1352
1402
|
};
|
|
1403
|
+
// Library / campaign consumers round-trip templateData via getTemplateData; include SMS fallback
|
|
1404
|
+
// so reopening the editor restores fallback text and tag mappings.
|
|
1405
|
+
// cap-campaigns-v2 API expects `smsFallBackContent.message` (see normalizeRcsMessageContentForApi).
|
|
1406
|
+
if (hasMeaningfulRcsSmsFallback(smsFallBackContent)) {
|
|
1407
|
+
const smsText =
|
|
1408
|
+
smsFallBackContent.message
|
|
1409
|
+
?? smsFallBackContent.smsContent
|
|
1410
|
+
?? smsFallBackContent.smsTemplateContent
|
|
1411
|
+
?? '';
|
|
1412
|
+
templateData.smsFallBackContent = {
|
|
1413
|
+
...smsFallBackContent,
|
|
1414
|
+
...(String(smsText).trim() !== ''
|
|
1415
|
+
? { message: String(smsText).trim() }
|
|
1416
|
+
: {}),
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
normalizeRcsMessageContentForApi(templateData);
|
|
1353
1420
|
}
|
|
1354
1421
|
break;
|
|
1355
1422
|
case constants.ZALO:
|
|
@@ -1467,7 +1534,10 @@ export class Creatives extends React.Component {
|
|
|
1467
1534
|
return templateData;
|
|
1468
1535
|
};
|
|
1469
1536
|
|
|
1470
|
-
getSlideBoxContent({ mode, templateData, isFullMode }) {
|
|
1537
|
+
getSlideBoxContent({ mode, templateData, isFullMode, useLocalTemplates }) {
|
|
1538
|
+
if (useLocalTemplates && mode === 'create' && isEmpty(templateData)) {
|
|
1539
|
+
return 'templates';
|
|
1540
|
+
}
|
|
1471
1541
|
let creativesMode = isFullMode ? 'createTemplate' : 'templates';// for library mode templates page is initial mode and for full mode createTemplates
|
|
1472
1542
|
if (mode === 'create' && isFullMode) {
|
|
1473
1543
|
creativesMode = 'createTemplate';
|
|
@@ -1555,24 +1625,110 @@ export class Creatives extends React.Component {
|
|
|
1555
1625
|
getFormData = (template) => {
|
|
1556
1626
|
// Always reset isGetFormData so the child does not re-send form data on every re-render
|
|
1557
1627
|
// (e.g. when user fixes validation error by typing, we must not auto-close the slidebox)
|
|
1558
|
-
this.setState(
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
{
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1628
|
+
this.setState(
|
|
1629
|
+
(prevState) => {
|
|
1630
|
+
const next = { isGetFormData: false };
|
|
1631
|
+
if (!template.validity) {
|
|
1632
|
+
return next;
|
|
1633
|
+
}
|
|
1634
|
+
const baseTd = prevState.templateData || template;
|
|
1635
|
+
const channel = (
|
|
1636
|
+
baseTd?.type
|
|
1637
|
+
|| template?.type
|
|
1638
|
+
|| get(template, 'value.type')
|
|
1639
|
+
|| ''
|
|
1640
|
+
).toUpperCase();
|
|
1641
|
+
// Library mode: persist last submitted creatives shape so reopening still hydrates the editor
|
|
1642
|
+
// (parent may not merge getCreativesData back into templateData).
|
|
1643
|
+
if (this.props.isFullMode === false && template.value) {
|
|
1644
|
+
if (channel === constants.RCS) {
|
|
1645
|
+
const smsFallBackFromPayload = get(
|
|
1646
|
+
template.value,
|
|
1647
|
+
'versions.base.content.RCS.smsFallBackContent',
|
|
1648
|
+
);
|
|
1649
|
+
const rcsCardVarMappedFromPayload = get(
|
|
1650
|
+
template.value,
|
|
1651
|
+
'versions.base.content.RCS.rcsContent.cardContent[0].cardVarMapped',
|
|
1652
|
+
);
|
|
1653
|
+
next.templateData = {
|
|
1654
|
+
...baseTd,
|
|
1655
|
+
type: constants.RCS,
|
|
1656
|
+
name: template?.value?.name,
|
|
1657
|
+
versions: template?.value?.versions,
|
|
1658
|
+
...(smsFallBackFromPayload != null
|
|
1659
|
+
&& typeof smsFallBackFromPayload === 'object'
|
|
1660
|
+
&& Object.keys(smsFallBackFromPayload).length > 0
|
|
1661
|
+
? { smsFallBackContent: smsFallBackFromPayload }
|
|
1662
|
+
: {}),
|
|
1663
|
+
...(rcsCardVarMappedFromPayload != null
|
|
1664
|
+
&& typeof rcsCardVarMappedFromPayload === 'object'
|
|
1665
|
+
? { rcsCardVarMapped: rcsCardVarMappedFromPayload }
|
|
1666
|
+
: {}),
|
|
1667
|
+
};
|
|
1668
|
+
if (template._id) {
|
|
1669
|
+
next.templateData._id = template._id;
|
|
1670
|
+
}
|
|
1671
|
+
} else if (channel === constants.SMS) {
|
|
1672
|
+
const submittedSmsTemplateValue = template?.value;
|
|
1673
|
+
const smsVersions =
|
|
1674
|
+
submittedSmsTemplateValue?.history != null
|
|
1675
|
+
? submittedSmsTemplateValue
|
|
1676
|
+
: {
|
|
1677
|
+
base: submittedSmsTemplateValue?.base,
|
|
1678
|
+
history: submittedSmsTemplateValue?.base
|
|
1679
|
+
? [submittedSmsTemplateValue.base]
|
|
1680
|
+
: [],
|
|
1681
|
+
};
|
|
1682
|
+
next.templateData = {
|
|
1683
|
+
...baseTd,
|
|
1684
|
+
type: constants.SMS,
|
|
1685
|
+
name: baseTd?.name || 'Campaign message SMS content',
|
|
1686
|
+
versions: smsVersions,
|
|
1687
|
+
};
|
|
1688
|
+
if (template?._id) {
|
|
1689
|
+
next.templateData._id = template._id;
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
return next;
|
|
1694
|
+
},
|
|
1695
|
+
() => {
|
|
1696
|
+
if (!template.validity) {
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1699
|
+
const templateData = this.state.templateData ? this.state.templateData : template; //select existing or create new content
|
|
1700
|
+
const channelForConsumer = String(
|
|
1701
|
+
templateData.type
|
|
1702
|
+
|| template.type
|
|
1703
|
+
|| get(template, 'value.type')
|
|
1704
|
+
|| '',
|
|
1705
|
+
).toUpperCase();
|
|
1706
|
+
const creativesData = this.getCreativesData(
|
|
1707
|
+
channelForConsumer,
|
|
1708
|
+
template,
|
|
1709
|
+
this.state.templateData || template,
|
|
1710
|
+
);// convers data to consumer understandable format
|
|
1711
|
+
creativesData.then((data) => {
|
|
1712
|
+
this.logGTMEvent(channelForConsumer, data);
|
|
1713
|
+
this.processCentralCommsMetaId(channelForConsumer, data, {
|
|
1714
|
+
closeSlideBoxAfterSubmit: template.closeSlideBoxAfterSubmit,
|
|
1569
1715
|
});
|
|
1570
|
-
}
|
|
1571
|
-
|
|
1572
|
-
|
|
1716
|
+
});
|
|
1717
|
+
},
|
|
1718
|
+
);
|
|
1573
1719
|
}
|
|
1574
1720
|
|
|
1575
|
-
processCentralCommsMetaId = (channel, creativesData) => {
|
|
1721
|
+
processCentralCommsMetaId = (channel, creativesData, options = {}) => {
|
|
1722
|
+
const { closeSlideBoxAfterSubmit = false } = options;
|
|
1723
|
+
const maybeCloseLibrarySlideBox = () => {
|
|
1724
|
+
if (
|
|
1725
|
+
closeSlideBoxAfterSubmit
|
|
1726
|
+
&& this.props.isFullMode === false
|
|
1727
|
+
&& typeof this.handleCloseSlideBox === 'function'
|
|
1728
|
+
) {
|
|
1729
|
+
this.handleCloseSlideBox();
|
|
1730
|
+
}
|
|
1731
|
+
};
|
|
1576
1732
|
// Create the payload for the centralcommnsmetaId API call
|
|
1577
1733
|
const { isLoyaltyModule = false, loyaltyMetaData = {} } = this.props;
|
|
1578
1734
|
const { actionName, setMetaData = () => { } } = loyaltyMetaData;
|
|
@@ -1598,6 +1754,7 @@ export class Creatives extends React.Component {
|
|
|
1598
1754
|
if (result?.status?.code === 200) {
|
|
1599
1755
|
setMetaData(result);
|
|
1600
1756
|
this.props.getCreativesData(creativesData);
|
|
1757
|
+
maybeCloseLibrarySlideBox();
|
|
1601
1758
|
} else {
|
|
1602
1759
|
CapNotification.error({ message: <FormattedMessage {...messages.somethingWentWrong} /> });
|
|
1603
1760
|
}
|
|
@@ -1608,6 +1765,7 @@ export class Creatives extends React.Component {
|
|
|
1608
1765
|
} else {
|
|
1609
1766
|
// If not a loyalty module or different action, should work as usual
|
|
1610
1767
|
this.props.getCreativesData(creativesData);
|
|
1768
|
+
maybeCloseLibrarySlideBox();
|
|
1611
1769
|
}
|
|
1612
1770
|
};
|
|
1613
1771
|
|
|
@@ -1640,7 +1798,9 @@ export class Creatives extends React.Component {
|
|
|
1640
1798
|
}
|
|
1641
1799
|
this.setState((prevState) => ({
|
|
1642
1800
|
...prevState,
|
|
1643
|
-
|
|
1801
|
+
// Library mode (isFullMode === false): retain last template so reopening still has RCS payload.
|
|
1802
|
+
// Undefined isFullMode defaults to full-mode close behavior (clear templateData).
|
|
1803
|
+
...(this.props.isFullMode !== false ? { templateData: undefined } : {}),
|
|
1644
1804
|
showSlideBox: false,
|
|
1645
1805
|
liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
|
|
1646
1806
|
isLiquidValidationError: false,
|
|
@@ -1797,22 +1957,30 @@ export class Creatives extends React.Component {
|
|
|
1797
1957
|
} />
|
|
1798
1958
|
)
|
|
1799
1959
|
|
|
1800
|
-
templateNameComponentInput = ({ formData, onFormDataChange, name }) =>
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1960
|
+
templateNameComponentInput = ({ formData, onFormDataChange, name }) => {
|
|
1961
|
+
// Use local state for immediate UI feedback, fallback to prop value
|
|
1962
|
+
const displayValue = this.state.localTemplateName !== '' ? this.state.localTemplateName : name;
|
|
1963
|
+
|
|
1964
|
+
return (
|
|
1965
|
+
<CapInput
|
|
1966
|
+
value={displayValue}
|
|
1967
|
+
suffix={<span />}
|
|
1968
|
+
onBlur={() => {
|
|
1969
|
+
this.setState({
|
|
1970
|
+
isEditName: false,
|
|
1971
|
+
localTemplateName: '', // Clear local state on blur
|
|
1972
|
+
}, () => {
|
|
1973
|
+
this.showTemplateName({ formData, onFormDataChange });
|
|
1974
|
+
});
|
|
1975
|
+
}}
|
|
1976
|
+
onChange={(ev) => {
|
|
1977
|
+
const { value } = ev.currentTarget;
|
|
1978
|
+
// Use optimized update for better performance
|
|
1979
|
+
this.updateTemplateNameImmediately(value, formData, onFormDataChange);
|
|
1980
|
+
}}
|
|
1981
|
+
/>
|
|
1982
|
+
);
|
|
1983
|
+
}
|
|
1816
1984
|
|
|
1817
1985
|
showTemplateName = ({ formData, onFormDataChange }) => { //gets called from email/index after template data is fetched
|
|
1818
1986
|
const {
|
|
@@ -1843,21 +2011,17 @@ export class Creatives extends React.Component {
|
|
|
1843
2011
|
}
|
|
1844
2012
|
|
|
1845
2013
|
showLiquidErrorInFooter = (errorMessagesFromFormBuilder, currentFormBuilderTab) => {
|
|
1846
|
-
const
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
this.setState(
|
|
1857
|
-
isLiquidValidationError,
|
|
1858
|
-
liquidErrorMessage: errorMessagesFromFormBuilder,
|
|
1859
|
-
activeFormBuilderTab: currentFormBuilderTab === 1 ? constants.ANDROID : (currentFormBuilderTab === 2 ? constants.IOS : null), // Update activeFormBuilderTab, default to 1 if undefined
|
|
1860
|
-
});
|
|
2014
|
+
const next = computeLiquidFooterUpdateFromFormBuilder(
|
|
2015
|
+
errorMessagesFromFormBuilder,
|
|
2016
|
+
this.state.liquidErrorMessage,
|
|
2017
|
+
currentFormBuilderTab,
|
|
2018
|
+
{
|
|
2019
|
+
previousIsLiquidValidationError: this.state.isLiquidValidationError,
|
|
2020
|
+
currentChannelUpper: this.state.currentChannel?.toUpperCase(),
|
|
2021
|
+
},
|
|
2022
|
+
);
|
|
2023
|
+
if (next == null) return;
|
|
2024
|
+
this.setState(next);
|
|
1861
2025
|
}
|
|
1862
2026
|
|
|
1863
2027
|
// Callback to update HTML Editor validation state (called from EmailWrapper)
|
|
@@ -1980,6 +2144,11 @@ export class Creatives extends React.Component {
|
|
|
1980
2144
|
inAppEditorType,
|
|
1981
2145
|
htmlEditorValidationState,
|
|
1982
2146
|
} = this.state;
|
|
2147
|
+
const useLocalTemplates = get(
|
|
2148
|
+
this.props,
|
|
2149
|
+
'localTemplatesConfig.useLocalTemplates',
|
|
2150
|
+
get(this.props, 'useLocalTemplates', false),
|
|
2151
|
+
);
|
|
1983
2152
|
const {
|
|
1984
2153
|
isFullMode,
|
|
1985
2154
|
creativesMode,
|
|
@@ -1998,7 +2167,6 @@ export class Creatives extends React.Component {
|
|
|
1998
2167
|
smsRegister,
|
|
1999
2168
|
enableNewChannels,
|
|
2000
2169
|
eventContextTags,
|
|
2001
|
-
waitEventContextTags = {},
|
|
2002
2170
|
isLoyaltyModule,
|
|
2003
2171
|
loyaltyMetaData = {},
|
|
2004
2172
|
} = this.props;
|
|
@@ -2032,14 +2200,7 @@ export class Creatives extends React.Component {
|
|
|
2032
2200
|
// IMPORTANT: Never show ErrorInfoNote in footer when in HTML Editor mode, even if liquidErrorMessage exists
|
|
2033
2201
|
const shouldShowErrorInfoNoteInFooter = isHTMLEditorMode ? false : hasBEEEditorErrors;
|
|
2034
2202
|
|
|
2035
|
-
|
|
2036
|
-
const slideBoxWrapperMargin = (get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0 && get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0)
|
|
2037
|
-
? CAP_SPACE_64
|
|
2038
|
-
: get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0
|
|
2039
|
-
? CAP_SPACE_56
|
|
2040
|
-
: get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0
|
|
2041
|
-
? CAP_SPACE_32
|
|
2042
|
-
: 0;
|
|
2203
|
+
const slideBoxWrapperMargin = getSlideBoxWrapperMarginFromLiquidErrors(liquidErrorMessage);
|
|
2043
2204
|
/* TODO: Instead of passing down same props separately to each component down, write common function to these props and pass it accordingly */
|
|
2044
2205
|
|
|
2045
2206
|
// Compute anonymous user type and channel restrictions
|
|
@@ -2068,7 +2229,10 @@ export class Creatives extends React.Component {
|
|
|
2068
2229
|
<SlideBoxWrapper
|
|
2069
2230
|
slideBoxWrapperMargin={slideBoxWrapperMargin}
|
|
2070
2231
|
shouldApplyFooterMargin={shouldShowErrorInfoNoteInFooter}
|
|
2071
|
-
className={classnames(
|
|
2232
|
+
className={classnames(
|
|
2233
|
+
`${classPrefix} ${isFullMode ? 'creatives-full-mode' : 'creatives-library-mode'} ${mapTemplateCreate ? 'map-template-create' : ''}`,
|
|
2234
|
+
useLocalTemplates && slidBoxContent === 'templates' && 'creatives-slidebox--local-sms-templates',
|
|
2235
|
+
)}
|
|
2072
2236
|
>
|
|
2073
2237
|
<CapSlideBox
|
|
2074
2238
|
header={
|
|
@@ -2093,12 +2257,13 @@ export class Creatives extends React.Component {
|
|
|
2093
2257
|
smsRegister={smsRegister}
|
|
2094
2258
|
handleClose={this.handleCloseSlideBox}
|
|
2095
2259
|
moduleType={this.props.messageDetails?.type}
|
|
2260
|
+
useLocalTemplates={useLocalTemplates}
|
|
2096
2261
|
/>
|
|
2097
2262
|
)}
|
|
2098
2263
|
content={(
|
|
2099
2264
|
<SlideBoxContent
|
|
2100
2265
|
key="creatives-container-slidebox-content"
|
|
2101
|
-
onSelectTemplate={this.onSelectTemplate}
|
|
2266
|
+
onSelectTemplate={this.props.onSelectTemplate != null ? this.props.onSelectTemplate : this.onSelectTemplate}
|
|
2102
2267
|
onCreateComplete={getCreativesData}
|
|
2103
2268
|
onPreviewTemplate={this.onPreviewTemplate}
|
|
2104
2269
|
slidBoxContent={slidBoxContent}
|
|
@@ -2166,7 +2331,6 @@ export class Creatives extends React.Component {
|
|
|
2166
2331
|
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.
|
|
2167
2332
|
hostName={this.props?.hostName || ''}
|
|
2168
2333
|
eventContextTags={eventContextTags}
|
|
2169
|
-
waitEventContextTags={waitEventContextTags}
|
|
2170
2334
|
isLoyaltyModule={isLoyaltyModule}
|
|
2171
2335
|
loyaltyMetaData={loyaltyMetaData}
|
|
2172
2336
|
showTestAndPreviewSlidebox={showTestAndPreviewSlidebox}
|
|
@@ -2175,7 +2339,8 @@ export class Creatives extends React.Component {
|
|
|
2175
2339
|
isTestAndPreviewMode={(() => this.state.isTestAndPreviewMode)()}
|
|
2176
2340
|
onHtmlEditorValidationStateChange={this.updateHtmlEditorValidationState}
|
|
2177
2341
|
onPersonalizationTokensChange={this.handlePersonalizationTokensChange}
|
|
2178
|
-
|
|
2342
|
+
localTemplatesConfig={pick(this.props.localTemplatesConfig || this.props, constants.LOCAL_TEMPLATE_CONFIG_KEYS)}
|
|
2343
|
+
/>
|
|
2179
2344
|
)}
|
|
2180
2345
|
footer={this.shouldShowFooter() ? (
|
|
2181
2346
|
<SlideBoxFooter
|
|
@@ -2260,13 +2425,31 @@ Creatives.propTypes = {
|
|
|
2260
2425
|
orgUnitId: PropTypes.number,
|
|
2261
2426
|
hostName: PropTypes.string,
|
|
2262
2427
|
eventContextTags: PropTypes.array,
|
|
2263
|
-
waitEventContextTags: PropTypes.object,
|
|
2264
2428
|
loyaltyTagFetchingDependencies: PropTypes.object,
|
|
2265
2429
|
customerType: PropTypes.string,
|
|
2266
2430
|
intl: PropTypes.shape({
|
|
2267
2431
|
formatMessage: PropTypes.func,
|
|
2268
2432
|
}),
|
|
2269
2433
|
stopValidation: PropTypes.func,
|
|
2434
|
+
// Local template list (e.g. for SMS fallback): when set, TemplatesV2 uses these instead of Redux.
|
|
2435
|
+
// All optional. Pass either localTemplatesConfig (object) or individual props below.
|
|
2436
|
+
localTemplatesConfig: PropTypes.shape({
|
|
2437
|
+
useLocalTemplates: PropTypes.bool,
|
|
2438
|
+
localTemplates: PropTypes.arrayOf(PropTypes.object),
|
|
2439
|
+
localTemplatesLoading: PropTypes.bool,
|
|
2440
|
+
localTemplatesFilterContent: PropTypes.node,
|
|
2441
|
+
localTemplatesSentinelContent: PropTypes.node,
|
|
2442
|
+
localTemplatesScrollContainerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
|
|
2443
|
+
localTemplatesUseSkeleton: PropTypes.bool,
|
|
2444
|
+
}),
|
|
2445
|
+
useLocalTemplates: PropTypes.bool,
|
|
2446
|
+
localTemplates: PropTypes.arrayOf(PropTypes.object),
|
|
2447
|
+
localTemplatesLoading: PropTypes.bool,
|
|
2448
|
+
localTemplatesFilterContent: PropTypes.node,
|
|
2449
|
+
localTemplatesSentinelContent: PropTypes.node,
|
|
2450
|
+
localTemplatesScrollContainerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
|
|
2451
|
+
localTemplatesUseSkeleton: PropTypes.bool,
|
|
2452
|
+
onSelectTemplate: PropTypes.func,
|
|
2270
2453
|
};
|
|
2271
2454
|
const mapStatesToProps = () => createStructuredSelector({
|
|
2272
2455
|
isLoading: isLoadingSelector(),
|