@capillarytech/creatives-library 8.0.359-alpha.0 → 8.0.359-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/index.html +1 -0
- package/package.json +1 -1
- package/services/tests/api.test.js +35 -20
- package/utils/cdnTransformation.js +75 -3
- package/utils/commonUtils.js +19 -1
- package/utils/rcsPayloadUtils.js +92 -0
- package/utils/templateVarUtils.js +201 -0
- package/utils/tests/cdnTransformation.test.js +127 -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/CapImageUpload/index.js +2 -2
- 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 +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 +79 -11
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
- package/v2Components/CommonTestAndPreview/UnifiedPreview/PreviewHeader.js +16 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +157 -15
- package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberPreviewContent.js +14 -132
- package/v2Components/CommonTestAndPreview/UnifiedPreview/WebPushPreviewContent.js +169 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +400 -239
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +202 -10
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
- package/v2Components/CommonTestAndPreview/constants.js +40 -2
- package/v2Components/CommonTestAndPreview/index.js +887 -453
- package/v2Components/CommonTestAndPreview/messages.js +45 -3
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
- package/v2Components/CommonTestAndPreview/sagas.js +25 -6
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +308 -284
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +231 -65
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
- package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +34 -13
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/PreviewHeader.test.js +163 -0
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/ViberPreviewContent.test.js +0 -364
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/WebPushPreviewContent.test.js +522 -0
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +454 -1
- package/v2Components/CommonTestAndPreview/tests/constants.test.js +2 -1
- package/v2Components/CommonTestAndPreview/tests/index.test.js +327 -4
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +31 -24
- package/v2Components/FormBuilder/index.js +167 -56
- 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 +37 -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/index.js +15 -3
- package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
- package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
- package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
- package/v2Components/VarSegmentMessageEditor/index.js +125 -0
- package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
- package/v2Containers/App/constants.js +3 -0
- package/v2Containers/App/tests/constants.test.js +61 -0
- 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 +9 -0
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +79 -0
- package/v2Containers/CreativesContainer/index.js +382 -127
- 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 +357 -98
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
- package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
- package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
- package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
- package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
- package/v2Containers/Rcs/constants.js +120 -11
- package/v2Containers/Rcs/index.js +2577 -812
- package/v2Containers/Rcs/index.scss +281 -8
- package/v2Containers/Rcs/messages.js +34 -3
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +225 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +98036 -70145
- 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 -86
- package/v2Containers/Templates/actions.js +11 -0
- package/v2Containers/Templates/constants.js +2 -0
- package/v2Containers/Templates/index.js +203 -145
- package/v2Containers/Templates/sagas.js +62 -13
- 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 +222 -22
- package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
- package/v2Containers/Templates/tests/webpush.test.js +375 -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/Viber/constants.js +0 -19
- package/v2Containers/Viber/index.js +47 -714
- package/v2Containers/Viber/index.scss +0 -148
- package/v2Containers/Viber/messages.js +0 -116
- package/v2Containers/Viber/tests/index.test.js +0 -80
- package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
- package/v2Containers/WebPush/Create/index.js +91 -8
- package/v2Containers/WebPush/Create/index.scss +7 -0
- package/v2Containers/WebPush/Create/tests/getTemplateContent.test.js +348 -0
- package/v2Containers/WebPush/Create/tests/testAndPreviewIntegration.test.js +325 -0
- package/v2Containers/Whatsapp/index.js +3 -20
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
|
@@ -1,9 +1,49 @@
|
|
|
1
1
|
import React from 'react';
|
|
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
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
|
+
import PropTypes from 'prop-types';
|
|
7
47
|
import CapSlideBox from '@capillarytech/cap-ui-library/CapSlideBox';
|
|
8
48
|
import CapHeader from '@capillarytech/cap-ui-library/CapHeader';
|
|
9
49
|
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
@@ -13,12 +53,11 @@ import CapNotification from '@capillarytech/cap-ui-library/CapNotification';
|
|
|
13
53
|
import { injectIntl, FormattedMessage } from 'react-intl';
|
|
14
54
|
import classnames from 'classnames';
|
|
15
55
|
import {
|
|
16
|
-
isEmpty, get, forEach, cloneDeep, debounce,
|
|
56
|
+
isEmpty, get, forEach, cloneDeep, debounce, pick,
|
|
17
57
|
} from 'lodash';
|
|
18
58
|
import { connect } from 'react-redux';
|
|
19
59
|
import { createStructuredSelector } from 'reselect';
|
|
20
60
|
import { bindActionCreators, compose } from 'redux';
|
|
21
|
-
import styled from 'styled-components';
|
|
22
61
|
import { GA } from '@capillarytech/cap-ui-utils';
|
|
23
62
|
import { DAEMON } from '@capillarytech/vulcan-react-sdk/utils/sagaInjectorTypes';
|
|
24
63
|
|
|
@@ -32,6 +71,7 @@ import SlideBoxContent from './SlideBoxContent';
|
|
|
32
71
|
import * as constants from './constants';
|
|
33
72
|
import * as commonUtil from '../../utils/common';
|
|
34
73
|
import { gtmPush } from '../../utils/gtmTrackers';
|
|
74
|
+
import { normalizeRcsMessageContentForApi } from '../../utils/rcsPayloadUtils';
|
|
35
75
|
import './index.scss';
|
|
36
76
|
import * as templateActions from '../Templates/actions';
|
|
37
77
|
import * as globalActions from '../Cap/actions';
|
|
@@ -47,6 +87,9 @@ import {
|
|
|
47
87
|
import {EXTERNAL_URL, SITE_URL, WEBPUSH_MEDIA_TYPES} from '../WebPush/constants';
|
|
48
88
|
import { IMAGE, VIDEO } from '../Facebook/Advertisement/constant';
|
|
49
89
|
import { RCS_STATUSES } 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';
|
|
50
93
|
import { CREATIVE } from '../Facebook/constants';
|
|
51
94
|
import { LOYALTY } from '../App/constants';
|
|
52
95
|
import {
|
|
@@ -61,6 +104,11 @@ import { capSagaForFetchSchemaForEntity, capSagaLiquidEntity } from '../Cap/saga
|
|
|
61
104
|
import { v2TemplateSagaWatchGetDefaultBeeTemplates } from '../Templates/sagas';
|
|
62
105
|
import { DYNAMIC_URL } from '../../v2Components/CapWhatsappCTA/constants';
|
|
63
106
|
import ErrorInfoNote from '../../v2Components/ErrorInfoNote';
|
|
107
|
+
import SlideBoxWrapper from './CreativesSlideBoxWrapper';
|
|
108
|
+
import {
|
|
109
|
+
computeLiquidFooterUpdateFromFormBuilder,
|
|
110
|
+
getSlideBoxWrapperMarginFromLiquidErrors,
|
|
111
|
+
} from './embeddedSlideboxUtils';
|
|
64
112
|
|
|
65
113
|
import {
|
|
66
114
|
transformChannelPayload,
|
|
@@ -69,51 +117,24 @@ import {
|
|
|
69
117
|
import { MANUAL_CAROUSEL } from '../MobilePushNew/constants';
|
|
70
118
|
import { BIG_HTML } from '../InApp/constants';
|
|
71
119
|
|
|
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
120
|
const classPrefix = 'add-creatives-section';
|
|
90
121
|
const CREATIVES_CONTAINER = 'creativesContainer';
|
|
91
122
|
|
|
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
123
|
export class Creatives extends React.Component {
|
|
113
124
|
constructor(props) {
|
|
114
125
|
super(props);
|
|
115
126
|
|
|
116
|
-
const
|
|
127
|
+
const useLocalTemplates = get(
|
|
128
|
+
props,
|
|
129
|
+
'localTemplatesConfig.useLocalTemplates',
|
|
130
|
+
get(props, 'useLocalTemplates', false),
|
|
131
|
+
);
|
|
132
|
+
const initialSlidBoxContent = this.getSlideBoxContent({
|
|
133
|
+
mode: props.creativesMode,
|
|
134
|
+
templateData: props.templateData,
|
|
135
|
+
isFullMode: props.isFullMode,
|
|
136
|
+
useLocalTemplates,
|
|
137
|
+
});
|
|
117
138
|
|
|
118
139
|
this.state = {
|
|
119
140
|
isLoadingContent: true,
|
|
@@ -160,7 +181,13 @@ export class Creatives extends React.Component {
|
|
|
160
181
|
}
|
|
161
182
|
|
|
162
183
|
componentWillUnmount() {
|
|
163
|
-
|
|
184
|
+
const isEmbedded = get(this.props, 'location.query.type', '') === "embedded";
|
|
185
|
+
const useLocalTemplates = get(
|
|
186
|
+
this.props,
|
|
187
|
+
'localTemplatesConfig.useLocalTemplates',
|
|
188
|
+
get(this.props, 'useLocalTemplates', false),
|
|
189
|
+
);
|
|
190
|
+
if (isEmbedded && !useLocalTemplates) {
|
|
164
191
|
this.props.templateActions.resetTemplateStoreData();
|
|
165
192
|
}
|
|
166
193
|
this.props.globalActions.clearMetaEntities();
|
|
@@ -768,15 +795,69 @@ export class Creatives extends React.Component {
|
|
|
768
795
|
smsFallBackContent = {},
|
|
769
796
|
creativeName = "",
|
|
770
797
|
channel = constants.RCS,
|
|
798
|
+
rcsCardVarMapped,
|
|
771
799
|
accountId = "",
|
|
772
800
|
} = templateData || {};
|
|
773
|
-
const
|
|
801
|
+
const { isFullMode: isFullModeForRcsPayload } = this.props;
|
|
802
|
+
const isCarouselRcs = (rcsContent?.cardType || '').toString().toLowerCase() === 'carousel';
|
|
803
|
+
const firstCardIn = (rcsContent.cardContent && rcsContent.cardContent[0]) || {};
|
|
804
|
+
const {
|
|
805
|
+
cardDisplayTitle: _omitDispTitleIn,
|
|
806
|
+
cardDisplayDescription: _omitDispDescIn,
|
|
807
|
+
...cardContent
|
|
808
|
+
} = firstCardIn;
|
|
774
809
|
const Status = RCS_STATUSES.approved || '';
|
|
810
|
+
const mergedCardVarMapped = (() => {
|
|
811
|
+
const nestedCardVarMapped = cardContent?.cardVarMapped;
|
|
812
|
+
const rootMirrorCardVarMapped = rcsCardVarMapped;
|
|
813
|
+
const nestedRecord =
|
|
814
|
+
nestedCardVarMapped != null && typeof nestedCardVarMapped === 'object'
|
|
815
|
+
? nestedCardVarMapped
|
|
816
|
+
: {};
|
|
817
|
+
const rootRecord =
|
|
818
|
+
rootMirrorCardVarMapped != null && typeof rootMirrorCardVarMapped === 'object'
|
|
819
|
+
? rootMirrorCardVarMapped
|
|
820
|
+
: {};
|
|
821
|
+
const mergedFromRootAndNested = {
|
|
822
|
+
...pickRcsCardVarMappedEntries(rootRecord),
|
|
823
|
+
...pickRcsCardVarMappedEntries(nestedRecord),
|
|
824
|
+
};
|
|
825
|
+
return Object.keys(mergedFromRootAndNested).length > 0
|
|
826
|
+
? mergedFromRootAndNested
|
|
827
|
+
: null;
|
|
828
|
+
})();
|
|
829
|
+
// Campaigns (embedded): do not duplicate `cardVarMapped` as root `rcsCardVarMapped` on send —
|
|
830
|
+
// slot map stays on `versions…cardContent[0].cardVarMapped` only. Library full mode keeps root mirror.
|
|
831
|
+
// Use `=== true` so omitted/undefined `isFullMode` does not behave like library (avoids duplicate on approval payload).
|
|
832
|
+
const includeRootRcsCardVarMapped =
|
|
833
|
+
mergedCardVarMapped && isFullModeForRcsPayload === true;
|
|
834
|
+
|
|
835
|
+
const builtCardContent = isCarouselRcs
|
|
836
|
+
? (rcsContent.cardContent || []).map((card, idx) => {
|
|
837
|
+
const {
|
|
838
|
+
cardDisplayTitle: _dt,
|
|
839
|
+
cardDisplayDescription: _dd,
|
|
840
|
+
...restCard
|
|
841
|
+
} = card || {};
|
|
842
|
+
return {
|
|
843
|
+
...restCard,
|
|
844
|
+
...(idx === 0 && mergedCardVarMapped ? { cardVarMapped: mergedCardVarMapped } : {}),
|
|
845
|
+
Status,
|
|
846
|
+
};
|
|
847
|
+
})
|
|
848
|
+
: [
|
|
849
|
+
{
|
|
850
|
+
...cardContent,
|
|
851
|
+
...(mergedCardVarMapped ? { cardVarMapped: mergedCardVarMapped } : {}),
|
|
852
|
+
Status,
|
|
853
|
+
},
|
|
854
|
+
];
|
|
775
855
|
|
|
776
856
|
creativesTemplateData = {
|
|
777
857
|
type: channel,
|
|
778
858
|
edit: true,
|
|
779
859
|
name: creativeName,
|
|
860
|
+
...(includeRootRcsCardVarMapped ? { rcsCardVarMapped: mergedCardVarMapped } : {}),
|
|
780
861
|
versions: {
|
|
781
862
|
base: {
|
|
782
863
|
content: {
|
|
@@ -784,12 +865,7 @@ export class Creatives extends React.Component {
|
|
|
784
865
|
rcsContent: {
|
|
785
866
|
...rcsContent,
|
|
786
867
|
...(accountId && !isFullMode && { accountId }),
|
|
787
|
-
cardContent:
|
|
788
|
-
{
|
|
789
|
-
...cardContent,
|
|
790
|
-
Status,
|
|
791
|
-
},
|
|
792
|
-
],
|
|
868
|
+
cardContent: builtCardContent,
|
|
793
869
|
},
|
|
794
870
|
smsFallBackContent,
|
|
795
871
|
},
|
|
@@ -937,7 +1013,10 @@ export class Creatives extends React.Component {
|
|
|
937
1013
|
return newExpandableDetails;
|
|
938
1014
|
}
|
|
939
1015
|
|
|
940
|
-
getCreativesData = async (
|
|
1016
|
+
getCreativesData = async (channelParam, template, templateRecords) => { //from creatives to consumers
|
|
1017
|
+
const channel = String(
|
|
1018
|
+
channelParam || template?.type || get(template, 'value.type') || '',
|
|
1019
|
+
).toUpperCase();
|
|
941
1020
|
let templateData = { channel };
|
|
942
1021
|
switch (channel) {
|
|
943
1022
|
case constants.SMS:
|
|
@@ -1284,28 +1363,104 @@ export class Creatives extends React.Component {
|
|
|
1284
1363
|
break;
|
|
1285
1364
|
case constants.RCS:
|
|
1286
1365
|
if (template.value) {
|
|
1287
|
-
const {
|
|
1288
|
-
} = template.value || {};
|
|
1289
|
-
const
|
|
1366
|
+
const { isFullMode: isFullModeForRcsConsumerPayload } = this.props;
|
|
1367
|
+
const { name = "", versions = {} } = template.value || {};
|
|
1368
|
+
const fromSubmit = get(versions, 'base.content.RCS.smsFallBackContent', {});
|
|
1369
|
+
const fromRecords = {
|
|
1370
|
+
...(templateRecords?.smsFallBackContent || {}),
|
|
1371
|
+
...(get(templateRecords, 'versions.base.content.RCS.smsFallBackContent') || {}),
|
|
1372
|
+
};
|
|
1373
|
+
const hasMeaningfulRcsSmsFallback = (smsFallbackPayload) => {
|
|
1374
|
+
if (
|
|
1375
|
+
!smsFallbackPayload
|
|
1376
|
+
|| typeof smsFallbackPayload !== 'object'
|
|
1377
|
+
|| Object.keys(smsFallbackPayload).length === 0
|
|
1378
|
+
) {
|
|
1379
|
+
return false;
|
|
1380
|
+
}
|
|
1381
|
+
const fallbackBodyText = String(
|
|
1382
|
+
smsFallbackPayload.smsContent
|
|
1383
|
+
?? smsFallbackPayload.smsTemplateContent
|
|
1384
|
+
?? smsFallbackPayload.message
|
|
1385
|
+
?? smsFallbackPayload.content
|
|
1386
|
+
?? '',
|
|
1387
|
+
).trim();
|
|
1388
|
+
const fallbackTemplateName = String(
|
|
1389
|
+
smsFallbackPayload.smsTemplateName ?? smsFallbackPayload.templateName ?? '',
|
|
1390
|
+
).trim();
|
|
1391
|
+
const rcsSmsFallbackVarMapped =
|
|
1392
|
+
smsFallbackPayload?.[RCS_SMS_FALLBACK_VAR_MAPPED_PROP];
|
|
1393
|
+
const hasVarMappedEntries =
|
|
1394
|
+
rcsSmsFallbackVarMapped != null
|
|
1395
|
+
&& typeof rcsSmsFallbackVarMapped === 'object'
|
|
1396
|
+
&& Object.keys(rcsSmsFallbackVarMapped).length > 0;
|
|
1397
|
+
return (
|
|
1398
|
+
fallbackBodyText !== ''
|
|
1399
|
+
|| fallbackTemplateName !== ''
|
|
1400
|
+
|| hasVarMappedEntries
|
|
1401
|
+
);
|
|
1402
|
+
};
|
|
1403
|
+
// If submit has only empty strings, do not let it wipe fallback mirrored on templateRecords (library round-trip).
|
|
1404
|
+
const smsFallBackContent = hasMeaningfulRcsSmsFallback(fromSubmit)
|
|
1405
|
+
? { ...fromRecords, ...fromSubmit }
|
|
1406
|
+
: { ...fromSubmit, ...fromRecords };
|
|
1290
1407
|
const {
|
|
1291
|
-
cardContent = [],
|
|
1408
|
+
cardContent: cardContentFromSubmit = [],
|
|
1292
1409
|
contentType = "",
|
|
1293
1410
|
cardType = "",
|
|
1294
1411
|
cardSettings = {},
|
|
1295
1412
|
accountId = "",
|
|
1296
1413
|
} = get(versions, 'base.content.RCS.rcsContent', {});
|
|
1414
|
+
const rootRcsCardVarMappedFromSubmit = get(template, 'value.rcsCardVarMapped');
|
|
1415
|
+
const firstCardFromSubmit = Array.isArray(cardContentFromSubmit)
|
|
1416
|
+
? cardContentFromSubmit[0]
|
|
1417
|
+
: null;
|
|
1418
|
+
const cardContent = mapRcsCardContentForConsumerWithResolvedTags(
|
|
1419
|
+
cardContentFromSubmit,
|
|
1420
|
+
rootRcsCardVarMappedFromSubmit,
|
|
1421
|
+
isFullModeForRcsConsumerPayload,
|
|
1422
|
+
);
|
|
1297
1423
|
const rcsContent = {
|
|
1298
1424
|
contentType,
|
|
1299
1425
|
cardType,
|
|
1300
1426
|
cardSettings,
|
|
1301
1427
|
cardContent,
|
|
1302
1428
|
};
|
|
1429
|
+
const cardVarMappedFromFirstRcsCard =
|
|
1430
|
+
firstCardFromSubmit?.cardVarMapped != null
|
|
1431
|
+
&& typeof firstCardFromSubmit.cardVarMapped === 'object'
|
|
1432
|
+
? pickRcsCardVarMappedEntries(firstCardFromSubmit.cardVarMapped)
|
|
1433
|
+
: null;
|
|
1434
|
+
const includeRootRcsCardVarMappedOnConsumerPayload =
|
|
1435
|
+
cardVarMappedFromFirstRcsCard
|
|
1436
|
+
&& Object.keys(cardVarMappedFromFirstRcsCard).length > 0
|
|
1437
|
+
&& isFullModeForRcsConsumerPayload === true;
|
|
1303
1438
|
templateData = {
|
|
1304
1439
|
channel,
|
|
1305
1440
|
creativeName: name,
|
|
1306
1441
|
rcsContent,
|
|
1307
1442
|
accountId,
|
|
1443
|
+
...(includeRootRcsCardVarMappedOnConsumerPayload
|
|
1444
|
+
? { rcsCardVarMapped: cardVarMappedFromFirstRcsCard }
|
|
1445
|
+
: {}),
|
|
1308
1446
|
};
|
|
1447
|
+
// Library / campaign consumers round-trip templateData via getTemplateData; include SMS fallback
|
|
1448
|
+
// so reopening the editor restores fallback text and tag mappings.
|
|
1449
|
+
// cap-campaigns-v2 API expects `smsFallBackContent.message` (see normalizeRcsMessageContentForApi).
|
|
1450
|
+
if (hasMeaningfulRcsSmsFallback(smsFallBackContent)) {
|
|
1451
|
+
const smsText =
|
|
1452
|
+
smsFallBackContent.message
|
|
1453
|
+
?? smsFallBackContent.smsContent
|
|
1454
|
+
?? smsFallBackContent.smsTemplateContent
|
|
1455
|
+
?? '';
|
|
1456
|
+
templateData.smsFallBackContent = {
|
|
1457
|
+
...smsFallBackContent,
|
|
1458
|
+
...(String(smsText).trim() !== ''
|
|
1459
|
+
? { message: String(smsText).trim() }
|
|
1460
|
+
: {}),
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
normalizeRcsMessageContentForApi(templateData);
|
|
1309
1464
|
}
|
|
1310
1465
|
break;
|
|
1311
1466
|
case constants.ZALO:
|
|
@@ -1423,7 +1578,10 @@ export class Creatives extends React.Component {
|
|
|
1423
1578
|
return templateData;
|
|
1424
1579
|
};
|
|
1425
1580
|
|
|
1426
|
-
getSlideBoxContent({ mode, templateData, isFullMode }) {
|
|
1581
|
+
getSlideBoxContent({ mode, templateData, isFullMode, useLocalTemplates }) {
|
|
1582
|
+
if (useLocalTemplates && mode === 'create' && isEmpty(templateData)) {
|
|
1583
|
+
return 'templates';
|
|
1584
|
+
}
|
|
1427
1585
|
let creativesMode = isFullMode ? 'createTemplate' : 'templates';// for library mode templates page is initial mode and for full mode createTemplates
|
|
1428
1586
|
if (mode === 'create' && isFullMode) {
|
|
1429
1587
|
creativesMode = 'createTemplate';
|
|
@@ -1511,24 +1669,110 @@ export class Creatives extends React.Component {
|
|
|
1511
1669
|
getFormData = (template) => {
|
|
1512
1670
|
// Always reset isGetFormData so the child does not re-send form data on every re-render
|
|
1513
1671
|
// (e.g. when user fixes validation error by typing, we must not auto-close the slidebox)
|
|
1514
|
-
this.setState(
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
{
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1672
|
+
this.setState(
|
|
1673
|
+
(prevState) => {
|
|
1674
|
+
const next = { isGetFormData: false };
|
|
1675
|
+
if (!template.validity) {
|
|
1676
|
+
return next;
|
|
1677
|
+
}
|
|
1678
|
+
const baseTd = prevState.templateData || template;
|
|
1679
|
+
const channel = (
|
|
1680
|
+
baseTd?.type
|
|
1681
|
+
|| template?.type
|
|
1682
|
+
|| get(template, 'value.type')
|
|
1683
|
+
|| ''
|
|
1684
|
+
).toUpperCase();
|
|
1685
|
+
// Library mode: persist last submitted creatives shape so reopening still hydrates the editor
|
|
1686
|
+
// (parent may not merge getCreativesData back into templateData).
|
|
1687
|
+
if (this.props.isFullMode === false && template.value) {
|
|
1688
|
+
if (channel === constants.RCS) {
|
|
1689
|
+
const smsFallBackFromPayload = get(
|
|
1690
|
+
template.value,
|
|
1691
|
+
'versions.base.content.RCS.smsFallBackContent',
|
|
1692
|
+
);
|
|
1693
|
+
const rcsCardVarMappedFromPayload = get(
|
|
1694
|
+
template.value,
|
|
1695
|
+
'versions.base.content.RCS.rcsContent.cardContent[0].cardVarMapped',
|
|
1696
|
+
);
|
|
1697
|
+
next.templateData = {
|
|
1698
|
+
...baseTd,
|
|
1699
|
+
type: constants.RCS,
|
|
1700
|
+
name: template?.value?.name,
|
|
1701
|
+
versions: template?.value?.versions,
|
|
1702
|
+
...(smsFallBackFromPayload != null
|
|
1703
|
+
&& typeof smsFallBackFromPayload === 'object'
|
|
1704
|
+
&& Object.keys(smsFallBackFromPayload).length > 0
|
|
1705
|
+
? { smsFallBackContent: smsFallBackFromPayload }
|
|
1706
|
+
: {}),
|
|
1707
|
+
...(rcsCardVarMappedFromPayload != null
|
|
1708
|
+
&& typeof rcsCardVarMappedFromPayload === 'object'
|
|
1709
|
+
? { rcsCardVarMapped: rcsCardVarMappedFromPayload }
|
|
1710
|
+
: {}),
|
|
1711
|
+
};
|
|
1712
|
+
if (template._id) {
|
|
1713
|
+
next.templateData._id = template._id;
|
|
1714
|
+
}
|
|
1715
|
+
} else if (channel === constants.SMS) {
|
|
1716
|
+
const submittedSmsTemplateValue = template?.value;
|
|
1717
|
+
const smsVersions =
|
|
1718
|
+
submittedSmsTemplateValue?.history != null
|
|
1719
|
+
? submittedSmsTemplateValue
|
|
1720
|
+
: {
|
|
1721
|
+
base: submittedSmsTemplateValue?.base,
|
|
1722
|
+
history: submittedSmsTemplateValue?.base
|
|
1723
|
+
? [submittedSmsTemplateValue.base]
|
|
1724
|
+
: [],
|
|
1725
|
+
};
|
|
1726
|
+
next.templateData = {
|
|
1727
|
+
...baseTd,
|
|
1728
|
+
type: constants.SMS,
|
|
1729
|
+
name: baseTd?.name || 'Campaign message SMS content',
|
|
1730
|
+
versions: smsVersions,
|
|
1731
|
+
};
|
|
1732
|
+
if (template?._id) {
|
|
1733
|
+
next.templateData._id = template._id;
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
return next;
|
|
1738
|
+
},
|
|
1739
|
+
() => {
|
|
1740
|
+
if (!template.validity) {
|
|
1741
|
+
return;
|
|
1742
|
+
}
|
|
1743
|
+
const templateData = this.state.templateData ? this.state.templateData : template; //select existing or create new content
|
|
1744
|
+
const channelForConsumer = String(
|
|
1745
|
+
templateData.type
|
|
1746
|
+
|| template.type
|
|
1747
|
+
|| get(template, 'value.type')
|
|
1748
|
+
|| '',
|
|
1749
|
+
).toUpperCase();
|
|
1750
|
+
const creativesData = this.getCreativesData(
|
|
1751
|
+
channelForConsumer,
|
|
1752
|
+
template,
|
|
1753
|
+
this.state.templateData || template,
|
|
1754
|
+
);// convers data to consumer understandable format
|
|
1755
|
+
creativesData.then((data) => {
|
|
1756
|
+
this.logGTMEvent(channelForConsumer, data);
|
|
1757
|
+
this.processCentralCommsMetaId(channelForConsumer, data, {
|
|
1758
|
+
closeSlideBoxAfterSubmit: template.closeSlideBoxAfterSubmit,
|
|
1525
1759
|
});
|
|
1526
|
-
}
|
|
1527
|
-
|
|
1528
|
-
|
|
1760
|
+
});
|
|
1761
|
+
},
|
|
1762
|
+
);
|
|
1529
1763
|
}
|
|
1530
1764
|
|
|
1531
|
-
processCentralCommsMetaId = (channel, creativesData) => {
|
|
1765
|
+
processCentralCommsMetaId = (channel, creativesData, options = {}) => {
|
|
1766
|
+
const { closeSlideBoxAfterSubmit = false } = options;
|
|
1767
|
+
const maybeCloseLibrarySlideBox = () => {
|
|
1768
|
+
if (
|
|
1769
|
+
closeSlideBoxAfterSubmit
|
|
1770
|
+
&& this.props.isFullMode === false
|
|
1771
|
+
&& typeof this.handleCloseSlideBox === 'function'
|
|
1772
|
+
) {
|
|
1773
|
+
this.handleCloseSlideBox();
|
|
1774
|
+
}
|
|
1775
|
+
};
|
|
1532
1776
|
// Create the payload for the centralcommnsmetaId API call
|
|
1533
1777
|
const { isLoyaltyModule = false, loyaltyMetaData = {} } = this.props;
|
|
1534
1778
|
const { actionName, setMetaData = () => { } } = loyaltyMetaData;
|
|
@@ -1554,6 +1798,7 @@ export class Creatives extends React.Component {
|
|
|
1554
1798
|
if (result?.status?.code === 200) {
|
|
1555
1799
|
setMetaData(result);
|
|
1556
1800
|
this.props.getCreativesData(creativesData);
|
|
1801
|
+
maybeCloseLibrarySlideBox();
|
|
1557
1802
|
} else {
|
|
1558
1803
|
CapNotification.error({ message: <FormattedMessage {...messages.somethingWentWrong} /> });
|
|
1559
1804
|
}
|
|
@@ -1564,6 +1809,7 @@ export class Creatives extends React.Component {
|
|
|
1564
1809
|
} else {
|
|
1565
1810
|
// If not a loyalty module or different action, should work as usual
|
|
1566
1811
|
this.props.getCreativesData(creativesData);
|
|
1812
|
+
maybeCloseLibrarySlideBox();
|
|
1567
1813
|
}
|
|
1568
1814
|
};
|
|
1569
1815
|
|
|
@@ -1596,7 +1842,9 @@ export class Creatives extends React.Component {
|
|
|
1596
1842
|
}
|
|
1597
1843
|
this.setState((prevState) => ({
|
|
1598
1844
|
...prevState,
|
|
1599
|
-
|
|
1845
|
+
// Library mode (isFullMode === false): retain last template so reopening still has RCS payload.
|
|
1846
|
+
// Undefined isFullMode defaults to full-mode close behavior (clear templateData).
|
|
1847
|
+
...(this.props.isFullMode !== false ? { templateData: undefined } : {}),
|
|
1600
1848
|
showSlideBox: false,
|
|
1601
1849
|
liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
|
|
1602
1850
|
isLiquidValidationError: false,
|
|
@@ -1753,30 +2001,22 @@ export class Creatives extends React.Component {
|
|
|
1753
2001
|
} />
|
|
1754
2002
|
)
|
|
1755
2003
|
|
|
1756
|
-
templateNameComponentInput = ({ formData, onFormDataChange, name }) =>
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
onChange={(ev) => {
|
|
1773
|
-
const { value } = ev.currentTarget;
|
|
1774
|
-
// Use optimized update for better performance
|
|
1775
|
-
this.updateTemplateNameImmediately(value, formData, onFormDataChange);
|
|
1776
|
-
}}
|
|
1777
|
-
/>
|
|
1778
|
-
);
|
|
1779
|
-
}
|
|
2004
|
+
templateNameComponentInput = ({ formData, onFormDataChange, name }) => (
|
|
2005
|
+
<TemplateNameInputField
|
|
2006
|
+
initialValue={name}
|
|
2007
|
+
suffix={<span />}
|
|
2008
|
+
onBlur={(committedValue) => {
|
|
2009
|
+
this.performTemplateNameUpdate(committedValue, formData, onFormDataChange);
|
|
2010
|
+
this.setState({ isEditName: false });
|
|
2011
|
+
}}
|
|
2012
|
+
onChange={(value) => {
|
|
2013
|
+
const isEmptyTemplateName = !value.trim();
|
|
2014
|
+
if (this.state.isTemplateNameEmpty !== isEmptyTemplateName) {
|
|
2015
|
+
this.setState({ isTemplateNameEmpty: isEmptyTemplateName });
|
|
2016
|
+
}
|
|
2017
|
+
}}
|
|
2018
|
+
/>
|
|
2019
|
+
)
|
|
1780
2020
|
|
|
1781
2021
|
showTemplateName = ({ formData, onFormDataChange }) => { //gets called from email/index after template data is fetched
|
|
1782
2022
|
const {
|
|
@@ -1807,21 +2047,17 @@ export class Creatives extends React.Component {
|
|
|
1807
2047
|
}
|
|
1808
2048
|
|
|
1809
2049
|
showLiquidErrorInFooter = (errorMessagesFromFormBuilder, currentFormBuilderTab) => {
|
|
1810
|
-
const
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
this.setState(
|
|
1821
|
-
isLiquidValidationError,
|
|
1822
|
-
liquidErrorMessage: errorMessagesFromFormBuilder,
|
|
1823
|
-
activeFormBuilderTab: currentFormBuilderTab === 1 ? constants.ANDROID : (currentFormBuilderTab === 2 ? constants.IOS : null), // Update activeFormBuilderTab, default to 1 if undefined
|
|
1824
|
-
});
|
|
2050
|
+
const next = computeLiquidFooterUpdateFromFormBuilder(
|
|
2051
|
+
errorMessagesFromFormBuilder,
|
|
2052
|
+
this.state.liquidErrorMessage,
|
|
2053
|
+
currentFormBuilderTab,
|
|
2054
|
+
{
|
|
2055
|
+
previousIsLiquidValidationError: this.state.isLiquidValidationError,
|
|
2056
|
+
currentChannelUpper: this.state.currentChannel?.toUpperCase(),
|
|
2057
|
+
},
|
|
2058
|
+
);
|
|
2059
|
+
if (next == null) return;
|
|
2060
|
+
this.setState(next);
|
|
1825
2061
|
}
|
|
1826
2062
|
|
|
1827
2063
|
// Callback to update HTML Editor validation state (called from EmailWrapper)
|
|
@@ -1944,6 +2180,11 @@ export class Creatives extends React.Component {
|
|
|
1944
2180
|
inAppEditorType,
|
|
1945
2181
|
htmlEditorValidationState,
|
|
1946
2182
|
} = this.state;
|
|
2183
|
+
const useLocalTemplates = get(
|
|
2184
|
+
this.props,
|
|
2185
|
+
'localTemplatesConfig.useLocalTemplates',
|
|
2186
|
+
get(this.props, 'useLocalTemplates', false),
|
|
2187
|
+
);
|
|
1947
2188
|
const {
|
|
1948
2189
|
isFullMode,
|
|
1949
2190
|
creativesMode,
|
|
@@ -1962,7 +2203,6 @@ export class Creatives extends React.Component {
|
|
|
1962
2203
|
smsRegister,
|
|
1963
2204
|
enableNewChannels,
|
|
1964
2205
|
eventContextTags,
|
|
1965
|
-
waitEventContextTags = {},
|
|
1966
2206
|
isLoyaltyModule,
|
|
1967
2207
|
loyaltyMetaData = {},
|
|
1968
2208
|
} = this.props;
|
|
@@ -1996,14 +2236,7 @@ export class Creatives extends React.Component {
|
|
|
1996
2236
|
// IMPORTANT: Never show ErrorInfoNote in footer when in HTML Editor mode, even if liquidErrorMessage exists
|
|
1997
2237
|
const shouldShowErrorInfoNoteInFooter = isHTMLEditorMode ? false : hasBEEEditorErrors;
|
|
1998
2238
|
|
|
1999
|
-
|
|
2000
|
-
const slideBoxWrapperMargin = (get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0 && get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0)
|
|
2001
|
-
? CAP_SPACE_64
|
|
2002
|
-
: get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0
|
|
2003
|
-
? CAP_SPACE_56
|
|
2004
|
-
: get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0
|
|
2005
|
-
? CAP_SPACE_32
|
|
2006
|
-
: 0;
|
|
2239
|
+
const slideBoxWrapperMargin = getSlideBoxWrapperMarginFromLiquidErrors(liquidErrorMessage);
|
|
2007
2240
|
/* TODO: Instead of passing down same props separately to each component down, write common function to these props and pass it accordingly */
|
|
2008
2241
|
|
|
2009
2242
|
// Compute anonymous user type and channel restrictions
|
|
@@ -2032,7 +2265,10 @@ export class Creatives extends React.Component {
|
|
|
2032
2265
|
<SlideBoxWrapper
|
|
2033
2266
|
slideBoxWrapperMargin={slideBoxWrapperMargin}
|
|
2034
2267
|
shouldApplyFooterMargin={shouldShowErrorInfoNoteInFooter}
|
|
2035
|
-
className={classnames(
|
|
2268
|
+
className={classnames(
|
|
2269
|
+
`${classPrefix} ${isFullMode ? 'creatives-full-mode' : 'creatives-library-mode'} ${mapTemplateCreate ? 'map-template-create' : ''}`,
|
|
2270
|
+
useLocalTemplates && slidBoxContent === 'templates' && 'creatives-slidebox--local-sms-templates',
|
|
2271
|
+
)}
|
|
2036
2272
|
>
|
|
2037
2273
|
<CapSlideBox
|
|
2038
2274
|
header={
|
|
@@ -2057,12 +2293,13 @@ export class Creatives extends React.Component {
|
|
|
2057
2293
|
smsRegister={smsRegister}
|
|
2058
2294
|
handleClose={this.handleCloseSlideBox}
|
|
2059
2295
|
moduleType={this.props.messageDetails?.type}
|
|
2296
|
+
useLocalTemplates={useLocalTemplates}
|
|
2060
2297
|
/>
|
|
2061
2298
|
)}
|
|
2062
2299
|
content={(
|
|
2063
2300
|
<SlideBoxContent
|
|
2064
2301
|
key="creatives-container-slidebox-content"
|
|
2065
|
-
onSelectTemplate={this.onSelectTemplate}
|
|
2302
|
+
onSelectTemplate={this.props.onSelectTemplate != null ? this.props.onSelectTemplate : this.onSelectTemplate}
|
|
2066
2303
|
onCreateComplete={getCreativesData}
|
|
2067
2304
|
onPreviewTemplate={this.onPreviewTemplate}
|
|
2068
2305
|
slidBoxContent={slidBoxContent}
|
|
@@ -2130,7 +2367,6 @@ export class Creatives extends React.Component {
|
|
|
2130
2367
|
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.
|
|
2131
2368
|
hostName={this.props?.hostName || ''}
|
|
2132
2369
|
eventContextTags={eventContextTags}
|
|
2133
|
-
waitEventContextTags={waitEventContextTags}
|
|
2134
2370
|
isLoyaltyModule={isLoyaltyModule}
|
|
2135
2371
|
loyaltyMetaData={loyaltyMetaData}
|
|
2136
2372
|
showTestAndPreviewSlidebox={showTestAndPreviewSlidebox}
|
|
@@ -2139,7 +2375,8 @@ export class Creatives extends React.Component {
|
|
|
2139
2375
|
isTestAndPreviewMode={(() => this.state.isTestAndPreviewMode)()}
|
|
2140
2376
|
onHtmlEditorValidationStateChange={this.updateHtmlEditorValidationState}
|
|
2141
2377
|
onPersonalizationTokensChange={this.handlePersonalizationTokensChange}
|
|
2142
|
-
|
|
2378
|
+
localTemplatesConfig={pick(this.props.localTemplatesConfig || this.props, constants.LOCAL_TEMPLATE_CONFIG_KEYS)}
|
|
2379
|
+
/>
|
|
2143
2380
|
)}
|
|
2144
2381
|
footer={this.shouldShowFooter() ? (
|
|
2145
2382
|
<SlideBoxFooter
|
|
@@ -2224,13 +2461,31 @@ Creatives.propTypes = {
|
|
|
2224
2461
|
orgUnitId: PropTypes.number,
|
|
2225
2462
|
hostName: PropTypes.string,
|
|
2226
2463
|
eventContextTags: PropTypes.array,
|
|
2227
|
-
waitEventContextTags: PropTypes.object,
|
|
2228
2464
|
loyaltyTagFetchingDependencies: PropTypes.object,
|
|
2229
2465
|
customerType: PropTypes.string,
|
|
2230
2466
|
intl: PropTypes.shape({
|
|
2231
2467
|
formatMessage: PropTypes.func,
|
|
2232
2468
|
}),
|
|
2233
2469
|
stopValidation: PropTypes.func,
|
|
2470
|
+
// Local template list (e.g. for SMS fallback): when set, TemplatesV2 uses these instead of Redux.
|
|
2471
|
+
// All optional. Pass either localTemplatesConfig (object) or individual props below.
|
|
2472
|
+
localTemplatesConfig: PropTypes.shape({
|
|
2473
|
+
useLocalTemplates: PropTypes.bool,
|
|
2474
|
+
localTemplates: PropTypes.arrayOf(PropTypes.object),
|
|
2475
|
+
localTemplatesLoading: PropTypes.bool,
|
|
2476
|
+
localTemplatesFilterContent: PropTypes.node,
|
|
2477
|
+
localTemplatesSentinelContent: PropTypes.node,
|
|
2478
|
+
localTemplatesScrollContainerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
|
|
2479
|
+
localTemplatesUseSkeleton: PropTypes.bool,
|
|
2480
|
+
}),
|
|
2481
|
+
useLocalTemplates: PropTypes.bool,
|
|
2482
|
+
localTemplates: PropTypes.arrayOf(PropTypes.object),
|
|
2483
|
+
localTemplatesLoading: PropTypes.bool,
|
|
2484
|
+
localTemplatesFilterContent: PropTypes.node,
|
|
2485
|
+
localTemplatesSentinelContent: PropTypes.node,
|
|
2486
|
+
localTemplatesScrollContainerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
|
|
2487
|
+
localTemplatesUseSkeleton: PropTypes.bool,
|
|
2488
|
+
onSelectTemplate: PropTypes.func,
|
|
2234
2489
|
};
|
|
2235
2490
|
const mapStatesToProps = () => createStructuredSelector({
|
|
2236
2491
|
isLoading: isLoadingSelector(),
|