@capillarytech/creatives-library 8.0.345-alpha.14 → 8.0.345-alpha.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/constants/unified.js +29 -0
- package/package.json +1 -1
- package/services/tests/api.test.js +13 -0
- package/utils/commonUtils.js +19 -1
- package/utils/rcsPayloadUtils.js +92 -0
- package/utils/templateVarUtils.js +201 -0
- package/utils/tests/templateVarUtils.test.js +204 -0
- package/v2Components/CapActionButton/constants.js +7 -0
- package/v2Components/CapActionButton/index.js +167 -109
- package/v2Components/CapActionButton/index.scss +157 -6
- package/v2Components/CapActionButton/messages.js +19 -3
- package/v2Components/CapActionButton/tests/index.test.js +41 -17
- package/v2Components/CapTagList/index.js +10 -0
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -49
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +207 -21
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +85 -10
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +79 -11
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +160 -15
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js.rej +18 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +341 -76
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +133 -4
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
- package/v2Components/CommonTestAndPreview/constants.js +38 -2
- package/v2Components/CommonTestAndPreview/index.js +676 -186
- package/v2Components/CommonTestAndPreview/messages.js +49 -3
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
- package/v2Components/CommonTestAndPreview/sagas.js +15 -6
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +308 -284
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +231 -65
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
- package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +34 -13
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
- package/v2Components/CommonTestAndPreview/tests/index.test.js +132 -4
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
- package/v2Components/FormBuilder/index.js +8 -10
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +87 -0
- package/v2Components/SmsFallback/constants.js +73 -0
- package/v2Components/SmsFallback/index.js +955 -0
- package/v2Components/SmsFallback/index.scss +265 -0
- package/v2Components/SmsFallback/messages.js +78 -0
- package/v2Components/SmsFallback/smsFallbackUtils.js +118 -0
- package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
- package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
- package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
- package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +197 -0
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +277 -0
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
- package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
- package/v2Components/TemplatePreview/_templatePreview.scss +33 -23
- package/v2Components/TemplatePreview/constants.js +2 -0
- package/v2Components/TemplatePreview/index.js +143 -28
- package/v2Components/TemplatePreview/tests/index.test.js +142 -0
- package/v2Components/TestAndPreviewSlidebox/index.js +13 -1
- package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
- package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
- package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
- package/v2Components/VarSegmentMessageEditor/index.js +125 -0
- package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
- package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +43 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +36 -4
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +10 -1
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
- package/v2Containers/CreativesContainer/constants.js +9 -0
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +67 -0
- package/v2Containers/CreativesContainer/index.js +300 -103
- package/v2Containers/CreativesContainer/index.scss +51 -1
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +78 -34
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +79 -16
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +357 -98
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
- package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
- package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
- package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
- package/v2Containers/Email/reducer.js +3 -11
- package/v2Containers/Email/sagas.js +5 -9
- package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +0 -4
- package/v2Containers/Email/tests/sagas.test.js +3 -21
- package/v2Containers/Rcs/constants.js +119 -8
- package/v2Containers/Rcs/index.js +2379 -807
- package/v2Containers/Rcs/index.js.rej +1336 -0
- package/v2Containers/Rcs/index.scss +276 -6
- package/v2Containers/Rcs/index.scss.rej +74 -0
- package/v2Containers/Rcs/messages.js +38 -3
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +225 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +98018 -70073
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap.rej +128 -0
- package/v2Containers/Rcs/tests/index.test.js +152 -121
- package/v2Containers/Rcs/tests/mockData.js +38 -0
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +318 -0
- package/v2Containers/Rcs/tests/utils.test.js +646 -30
- package/v2Containers/Rcs/utils.js +478 -11
- package/v2Containers/Sms/Create/index.js +100 -40
- package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
- package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
- package/v2Containers/SmsTrai/Create/index.js +9 -4
- package/v2Containers/SmsTrai/Edit/constants.js +2 -0
- package/v2Containers/SmsTrai/Edit/index.js +636 -130
- package/v2Containers/SmsTrai/Edit/index.scss +121 -0
- package/v2Containers/SmsTrai/Edit/messages.js +14 -4
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4328 -2375
- package/v2Containers/SmsWrapper/index.js +37 -8
- package/v2Containers/TagList/index.js +6 -0
- package/v2Containers/Templates/TemplatesActionBar.js +101 -0
- package/v2Containers/Templates/_templates.scss +163 -2
- package/v2Containers/Templates/actions.js +11 -0
- package/v2Containers/Templates/constants.js +2 -0
- package/v2Containers/Templates/index.js +119 -54
- package/v2Containers/Templates/sagas.js +57 -12
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1043 -1079
- package/v2Containers/Templates/tests/sagas.test.js +193 -123
- package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
- package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
- package/v2Containers/TemplatesV2/index.js +86 -23
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
- package/v2Containers/Whatsapp/index.js +3 -20
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
GET_PREFILLED_VALUES_SUCCESS,
|
|
35
35
|
GET_PREFILLED_VALUES_FAILURE,
|
|
36
36
|
} from './constants';
|
|
37
|
+
import { extractPreviewFromLiquidResponse } from '../CommonTestAndPreview/previewApiUtils';
|
|
37
38
|
|
|
38
39
|
// Search Customers Saga
|
|
39
40
|
export function* searchCustomersSaga(action) {
|
|
@@ -80,11 +81,12 @@ export function* updatePreviewSaga(action) {
|
|
|
80
81
|
const customValues = action.payload.resolvedTags;
|
|
81
82
|
|
|
82
83
|
const response = yield call(Api.updateEmailPreview, action.payload);
|
|
83
|
-
|
|
84
|
+
const previewPayload = extractPreviewFromLiquidResponse(response);
|
|
85
|
+
if (previewPayload) {
|
|
84
86
|
yield put({
|
|
85
87
|
type: UPDATE_PREVIEW_SUCCESS,
|
|
86
88
|
payload: {
|
|
87
|
-
previewData:
|
|
89
|
+
previewData: previewPayload,
|
|
88
90
|
customValues, // Pass custom values to be preserved
|
|
89
91
|
},
|
|
90
92
|
});
|
|
@@ -221,8 +223,13 @@ export function* createMessageMetaSaga(action) {
|
|
|
221
223
|
export function* getPrefilledValuesSaga(action) {
|
|
222
224
|
try {
|
|
223
225
|
const response = yield call(Api.updateEmailPreview, action.payload);
|
|
224
|
-
|
|
225
|
-
|
|
226
|
+
const body =
|
|
227
|
+
response?.data !== undefined && response?.data !== null
|
|
228
|
+
? response.data
|
|
229
|
+
: response;
|
|
230
|
+
const resolvedTagValues = body?.resolvedTagValues;
|
|
231
|
+
if (resolvedTagValues != null) {
|
|
232
|
+
yield put({ type: GET_PREFILLED_VALUES_SUCCESS, payload: { values: resolvedTagValues } });
|
|
226
233
|
} else {
|
|
227
234
|
yield put({ type: GET_PREFILLED_VALUES_FAILURE, payload: { error: response.error || 'Failed to fetch prefilled values' } });
|
|
228
235
|
}
|
|
@@ -136,7 +136,9 @@ describe('TestAndPreviewSlidebox Sagas', () => {
|
|
|
136
136
|
describe('updatePreviewSaga', () => {
|
|
137
137
|
it('should handle successful preview update', () => {
|
|
138
138
|
const mockResponse = {
|
|
139
|
-
data:
|
|
139
|
+
data: {
|
|
140
|
+
resolvedBody: 'Test Preview Data',
|
|
141
|
+
},
|
|
140
142
|
};
|
|
141
143
|
const customValues = { test: 'value' };
|
|
142
144
|
return expectSaga(sagas.updatePreviewSaga, {
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared message editor that renders template text with {{var}} and/or DLT `{#var#}` segments as
|
|
3
|
+
* variable inputs and static text as headings.
|
|
4
|
+
* Reused by RCS (title/description), SmsTrai Edit (SMS fallback), and WhatsApp (edit message/header).
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import PropTypes from 'prop-types';
|
|
8
|
+
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
9
|
+
import CapHeading from '@capillarytech/cap-ui-library/CapHeading';
|
|
10
|
+
import CapInput from '@capillarytech/cap-ui-library/CapInput';
|
|
11
|
+
import {
|
|
12
|
+
splitTemplateVarString,
|
|
13
|
+
DEFAULT_MUSTACHE_VAR_REGEX,
|
|
14
|
+
isAnyTemplateVarToken,
|
|
15
|
+
} from '../../utils/templateVarUtils';
|
|
16
|
+
|
|
17
|
+
import './index.scss';
|
|
18
|
+
import { VAR_SEGMENT_PLACEHOLDER_PREFIX } from './constants';
|
|
19
|
+
|
|
20
|
+
const { TextArea } = CapInput;
|
|
21
|
+
|
|
22
|
+
export function VarSegmentMessageEditor({
|
|
23
|
+
templateString = '',
|
|
24
|
+
valueMap = {},
|
|
25
|
+
onChange,
|
|
26
|
+
onFocus,
|
|
27
|
+
placeholderPrefix = VAR_SEGMENT_PLACEHOLDER_PREFIX,
|
|
28
|
+
getPlaceholder,
|
|
29
|
+
wrapperClassName = 'rcs_text_area_wrapper',
|
|
30
|
+
rowClassName = 'rcs-edit-template-message-input',
|
|
31
|
+
headingClassName = 'rcs-edit-template-message-split',
|
|
32
|
+
varRegex,
|
|
33
|
+
readOnly = false,
|
|
34
|
+
disabled = false,
|
|
35
|
+
footerContent,
|
|
36
|
+
renderVarFooter,
|
|
37
|
+
}) {
|
|
38
|
+
const segments = splitTemplateVarString(templateString, varRegex || DEFAULT_MUSTACHE_VAR_REGEX);
|
|
39
|
+
if (!segments?.length) return null;
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className={wrapperClassName}>
|
|
43
|
+
<CapRow className={rowClassName}>
|
|
44
|
+
{segments.map((segmentToken, segmentIndex) => {
|
|
45
|
+
const isVar =
|
|
46
|
+
typeof segmentToken === 'string' && isAnyTemplateVarToken(segmentToken);
|
|
47
|
+
if (isVar) {
|
|
48
|
+
const varSegmentFieldId = `${segmentToken}_${segmentIndex}`;
|
|
49
|
+
const slotValueFromMap = valueMap?.[varSegmentFieldId];
|
|
50
|
+
// Missing key: show empty (not the raw {{…}} token) so cleared slots and incomplete maps
|
|
51
|
+
// cannot resurrect the token; placeholder still guides the user.
|
|
52
|
+
const value =
|
|
53
|
+
slotValueFromMap !== undefined && slotValueFromMap !== null ? slotValueFromMap : '';
|
|
54
|
+
if (readOnly) {
|
|
55
|
+
return (
|
|
56
|
+
<CapHeading
|
|
57
|
+
key={varSegmentFieldId}
|
|
58
|
+
type="h4"
|
|
59
|
+
className={`${headingClassName} var-segment-message-editor__read-only-value`.trim()}
|
|
60
|
+
>
|
|
61
|
+
{value}
|
|
62
|
+
</CapHeading>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
const fromGet = getPlaceholder && getPlaceholder(segmentToken, segmentIndex);
|
|
66
|
+
const placeholder =
|
|
67
|
+
fromGet !== undefined && fromGet !== null && fromGet !== ''
|
|
68
|
+
? fromGet
|
|
69
|
+
: `${placeholderPrefix}${segmentToken}`;
|
|
70
|
+
return (
|
|
71
|
+
<div key={varSegmentFieldId} className="var-segment-message-editor__var-slot">
|
|
72
|
+
<TextArea
|
|
73
|
+
id={varSegmentFieldId}
|
|
74
|
+
placeholder={placeholder}
|
|
75
|
+
autosize={{ minRows: 1, maxRows: 3 }}
|
|
76
|
+
value={value}
|
|
77
|
+
onFocus={() => onFocus && onFocus(varSegmentFieldId)}
|
|
78
|
+
onChange={(e) =>
|
|
79
|
+
onChange && onChange(varSegmentFieldId, e?.target?.value ?? '')}
|
|
80
|
+
disabled={disabled}
|
|
81
|
+
/>
|
|
82
|
+
{renderVarFooter
|
|
83
|
+
? renderVarFooter(segmentToken, segmentIndex, varSegmentFieldId)
|
|
84
|
+
: null}
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
if (segmentToken) {
|
|
89
|
+
return (
|
|
90
|
+
<CapHeading
|
|
91
|
+
key={`static_${segmentIndex}_${segmentToken}`}
|
|
92
|
+
type="h4"
|
|
93
|
+
className={headingClassName}
|
|
94
|
+
>
|
|
95
|
+
{segmentToken}
|
|
96
|
+
</CapHeading>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
})}
|
|
101
|
+
</CapRow>
|
|
102
|
+
{footerContent}
|
|
103
|
+
</div>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
VarSegmentMessageEditor.propTypes = {
|
|
108
|
+
templateString: PropTypes.string,
|
|
109
|
+
valueMap: PropTypes.object,
|
|
110
|
+
onChange: PropTypes.func,
|
|
111
|
+
onFocus: PropTypes.func,
|
|
112
|
+
placeholderPrefix: PropTypes.string,
|
|
113
|
+
getPlaceholder: PropTypes.func,
|
|
114
|
+
wrapperClassName: PropTypes.string,
|
|
115
|
+
rowClassName: PropTypes.string,
|
|
116
|
+
headingClassName: PropTypes.string,
|
|
117
|
+
varRegex: PropTypes.object,
|
|
118
|
+
readOnly: PropTypes.bool,
|
|
119
|
+
disabled: PropTypes.bool,
|
|
120
|
+
footerContent: PropTypes.node,
|
|
121
|
+
/** Optional hint below a variable field (e.g. DLT `{#var#}` max length). */
|
|
122
|
+
renderVarFooter: PropTypes.func,
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export default VarSegmentMessageEditor;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
@import '~@capillarytech/cap-ui-library/styles/_variables';
|
|
2
|
+
|
|
3
|
+
/* Same look as RCS edit message block: background, spacing, text color */
|
|
4
|
+
.rcs_text_area_wrapper {
|
|
5
|
+
.rcs-edit-template-message-input {
|
|
6
|
+
background-color: $CAP_G10;
|
|
7
|
+
padding: $CAP_SPACE_12 $CAP_SPACE_16 $CAP_SPACE_16;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.rcs-edit-template-message-split {
|
|
11
|
+
margin: 0 0 $CAP_SPACE_04 0;
|
|
12
|
+
overflow: hidden;
|
|
13
|
+
text-overflow: ellipsis;
|
|
14
|
+
color: $FONT_COLOR_04;
|
|
15
|
+
font-weight: 500;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* Variable chips: match RCS edit (white field, light border, 4px radius) */
|
|
19
|
+
.rcs-edit-template-message-input .ant-input,
|
|
20
|
+
.rcs-edit-template-message-input textarea.ant-input {
|
|
21
|
+
margin: 0 0 0.125rem 0;
|
|
22
|
+
border-radius: 0.25rem;
|
|
23
|
+
border: 0.0625rem solid $CAP_G07;
|
|
24
|
+
background-color: $CAP_WHITE;
|
|
25
|
+
overflow: hidden;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* Small gap between tag border and the next line (static text) */
|
|
29
|
+
.rcs-edit-template-message-input :not(:first-child) {
|
|
30
|
+
margin-top: $CAP_SPACE_08;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.rcs-edit-template-message-input > *:last-child {
|
|
34
|
+
margin-bottom: 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.var-segment-message-editor__var-slot {
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
width: 100%;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.var-segment-message-editor__read-only-value {
|
|
45
|
+
margin: 0;
|
|
46
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Styled wrapper for CapSlideBox used by CreativesContainer and RCS SMS fallback
|
|
3
|
+
* so header/content/footer margins match.
|
|
4
|
+
*/
|
|
5
|
+
import styled from 'styled-components';
|
|
6
|
+
import { CAP_SPACE_16 } from '@capillarytech/cap-ui-library/styled/variables';
|
|
7
|
+
|
|
8
|
+
const CreativesSlideBoxWrapper = styled.div`
|
|
9
|
+
.cap-slide-box-v2-container {
|
|
10
|
+
/*
|
|
11
|
+
* Liquid-error spacing must stay *inside* the content column. margin-bottom on
|
|
12
|
+
* .slidebox-content-container added to the in-flow height past 100vh, so the outer
|
|
13
|
+
* .cap-slide-box-v2-container (overflow-y: auto in cap-ui) gained a second scrollbar.
|
|
14
|
+
*/
|
|
15
|
+
.slidebox-header {
|
|
16
|
+
margin-bottom: ${({ slideBoxWrapperMargin }) => `${slideBoxWrapperMargin}`};
|
|
17
|
+
padding: 0 rem;
|
|
18
|
+
&.has-footer {
|
|
19
|
+
overflow-x: hidden;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
.slidebox-content-container {
|
|
23
|
+
margin-bottom: 0;
|
|
24
|
+
padding: 0 rem;
|
|
25
|
+
padding-bottom: ${({ slideBoxWrapperMargin }) => `${slideBoxWrapperMargin}`};
|
|
26
|
+
box-sizing: border-box;
|
|
27
|
+
&.has-footer {
|
|
28
|
+
overflow-x: hidden;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
.slidebox-footer {
|
|
32
|
+
/* Only apply margin-bottom to footer when ErrorInfoNote is shown in footer (BEE editor) */
|
|
33
|
+
/* For HTML Editor, errors are shown in ValidationErrorDisplay (inside content area), so no footer margin needed */
|
|
34
|
+
margin-bottom: ${({ shouldApplyFooterMargin }) => (shouldApplyFooterMargin ? `${CAP_SPACE_16}` : '0')};
|
|
35
|
+
padding: 0 rem;
|
|
36
|
+
&.has-footer {
|
|
37
|
+
overflow-x: hidden;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
export default CreativesSlideBoxWrapper;
|
|
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
|
|
3
3
|
import styled from 'styled-components';
|
|
4
4
|
import get from 'lodash/get';
|
|
5
5
|
import isEmpty from 'lodash/isEmpty';
|
|
6
|
+
import pick from 'lodash/pick';
|
|
6
7
|
import cloneDeep from 'lodash/cloneDeep';
|
|
7
8
|
import TemplatesV2 from '../TemplatesV2';
|
|
8
9
|
import TemplatePreview from '../../v2Components/TemplatePreview';
|
|
@@ -25,6 +26,7 @@ import Viber from '../Viber';
|
|
|
25
26
|
import Whatsapp from '../Whatsapp';
|
|
26
27
|
import InApp from '../InApp';
|
|
27
28
|
import Rcs from '../Rcs';
|
|
29
|
+
import { isRcsTextOnlyCardMediaType, resolveRcsCardPreviewStrings } from '../Rcs/utils';
|
|
28
30
|
import { getWhatsappContent } from '../Whatsapp/utils';
|
|
29
31
|
import * as commonUtil from '../../utils/common';
|
|
30
32
|
import Zalo from '../Zalo';
|
|
@@ -180,6 +182,8 @@ export function SlideBoxContent(props) {
|
|
|
180
182
|
isTestAndPreviewMode,
|
|
181
183
|
onHtmlEditorValidationStateChange,
|
|
182
184
|
} = props;
|
|
185
|
+
const localTemplatesConfig = props.localTemplatesConfig || pick(props, constants.LOCAL_TEMPLATE_CONFIG_KEYS);
|
|
186
|
+
const useLocalTemplates = !!get(localTemplatesConfig, 'useLocalTemplates');
|
|
183
187
|
const type = (messageDetails.type || '').toLowerCase(); // type is context in get tags values : outbound | dvs | referral | loyalty | coupons
|
|
184
188
|
const query = { type: !isFullMode && 'embedded', module: isFullMode ? 'default' : 'library', isEditFromCampaigns: (templateData || {}).isEditFromCampaigns};
|
|
185
189
|
const creativesLocationProps = {
|
|
@@ -399,12 +403,37 @@ export function SlideBoxContent(props) {
|
|
|
399
403
|
}
|
|
400
404
|
case constants.RCS: {
|
|
401
405
|
const template = cloneDeep(templateDataObject);
|
|
402
|
-
const
|
|
406
|
+
const cardPath = 'versions.base.content.RCS.rcsContent.cardContent[0]';
|
|
407
|
+
const card = get(template, cardPath, {}) || {};
|
|
408
|
+
const {
|
|
409
|
+
description = '',
|
|
410
|
+
media: { mediaUrl = '' } = {},
|
|
411
|
+
title = '',
|
|
412
|
+
mediaType: cardMediaType,
|
|
413
|
+
suggestions = [],
|
|
414
|
+
cardVarMapped: nestedCardVarMapped,
|
|
415
|
+
} = card;
|
|
416
|
+
const rootMirror = templateDataObject?.rcsCardVarMapped;
|
|
417
|
+
const nestedRecord =
|
|
418
|
+
nestedCardVarMapped != null && typeof nestedCardVarMapped === 'object'
|
|
419
|
+
? nestedCardVarMapped
|
|
420
|
+
: {};
|
|
421
|
+
const rootRecord =
|
|
422
|
+
rootMirror != null && typeof rootMirror === 'object' ? rootMirror : {};
|
|
423
|
+
const mergedCardVarMapped = { ...rootRecord, ...nestedRecord };
|
|
424
|
+
const textOnlyCard = isRcsTextOnlyCardMediaType(cardMediaType);
|
|
425
|
+
const { rcsTitle, rcsDesc } = resolveRcsCardPreviewStrings(
|
|
426
|
+
title,
|
|
427
|
+
description,
|
|
428
|
+
mergedCardVarMapped,
|
|
429
|
+
!isFullMode,
|
|
430
|
+
textOnlyCard,
|
|
431
|
+
);
|
|
403
432
|
return {
|
|
404
433
|
rcsPreviewContent: {
|
|
405
434
|
rcsImageSrc: mediaUrl,
|
|
406
|
-
rcsTitle
|
|
407
|
-
rcsDesc
|
|
435
|
+
rcsTitle,
|
|
436
|
+
rcsDesc,
|
|
408
437
|
...(suggestions.length > 0 && {
|
|
409
438
|
buttonText: suggestions[0]?.text,
|
|
410
439
|
}),
|
|
@@ -430,7 +459,7 @@ export function SlideBoxContent(props) {
|
|
|
430
459
|
|
|
431
460
|
return (
|
|
432
461
|
<CreativesWrapper>
|
|
433
|
-
{
|
|
462
|
+
{slidBoxContent === 'templates' && (!isFullMode || useLocalTemplates) && (
|
|
434
463
|
<TemplatesV2
|
|
435
464
|
isFullMode={isFullMode}
|
|
436
465
|
onSelectTemplate={onSelectTemplate}
|
|
@@ -463,6 +492,7 @@ export function SlideBoxContent(props) {
|
|
|
463
492
|
waitEventContextTags={waitEventContextTags}
|
|
464
493
|
loyaltyMetaData={loyaltyMetaData}
|
|
465
494
|
isLoyaltyModule={isLoyaltyModule}
|
|
495
|
+
localTemplatesConfig={localTemplatesConfig}
|
|
466
496
|
/>
|
|
467
497
|
)}
|
|
468
498
|
{isPreview && (
|
|
@@ -633,6 +663,7 @@ export function SlideBoxContent(props) {
|
|
|
633
663
|
route={{ name: 'sms' }}
|
|
634
664
|
isCreateSms={isCreateSms}
|
|
635
665
|
isComponent
|
|
666
|
+
templateData={templateData}
|
|
636
667
|
isGetFormData={isGetFormData}
|
|
637
668
|
getFormSubscriptionData={getFormData}
|
|
638
669
|
getLiquidTags={getLiquidTags}
|
|
@@ -1239,6 +1270,7 @@ export function SlideBoxContent(props) {
|
|
|
1239
1270
|
)}
|
|
1240
1271
|
{isCreateRcs && (<Rcs
|
|
1241
1272
|
{...rcsCommonProps}
|
|
1273
|
+
templateData={templateData}
|
|
1242
1274
|
showLiquidErrorInFooter={showLiquidErrorInFooter}
|
|
1243
1275
|
showTestAndPreviewSlidebox={showTestAndPreviewSlidebox}
|
|
1244
1276
|
handleTestAndPreview={handleTestAndPreview}
|
|
@@ -48,6 +48,8 @@ function SlideBoxFooter(props) {
|
|
|
48
48
|
isAnonymousType = false,
|
|
49
49
|
templateData = {},
|
|
50
50
|
hasPersonalizationTokenError: hasPersonalizationTokenErrorProp = false,
|
|
51
|
+
/** When set (e.g. SMS library create), overrides `creativesTemplatesSave` (“Done”) for the primary button */
|
|
52
|
+
primarySaveButtonMessage,
|
|
51
53
|
} = props;
|
|
52
54
|
// Calculate if buttons should be disabled
|
|
53
55
|
// Only apply validation state checks for EMAIL channel in HTML Editor mode (not BEE/DragDrop)
|
|
@@ -186,7 +188,9 @@ function SlideBoxFooter(props) {
|
|
|
186
188
|
onClick={onSave}
|
|
187
189
|
disabled={isTemplateNameEmpty || fetchingCmsData || shouldDisableButtons || hasPersonalizationTokenError}
|
|
188
190
|
>
|
|
189
|
-
{
|
|
191
|
+
{primarySaveButtonMessage ? (
|
|
192
|
+
<FormattedMessage {...primarySaveButtonMessage} />
|
|
193
|
+
) : isFullMode ? (
|
|
190
194
|
getFullModeSaveBtn(slidBoxContent, isCreatingTemplate)
|
|
191
195
|
) : (
|
|
192
196
|
<FormattedMessage {...messages.creativesTemplatesSave} />
|
|
@@ -262,6 +266,10 @@ SlideBoxFooter.propTypes = {
|
|
|
262
266
|
templateData: PropTypes.object,
|
|
263
267
|
formData: PropTypes.array,
|
|
264
268
|
hasPersonalizationTokenError: PropTypes.bool,
|
|
269
|
+
primarySaveButtonMessage: PropTypes.shape({
|
|
270
|
+
id: PropTypes.string,
|
|
271
|
+
defaultMessage: PropTypes.string,
|
|
272
|
+
}),
|
|
265
273
|
};
|
|
266
274
|
|
|
267
275
|
SlideBoxFooter.defaultProps = {
|
|
@@ -289,5 +297,6 @@ SlideBoxFooter.defaultProps = {
|
|
|
289
297
|
selectedEmailCreateMode: '',
|
|
290
298
|
formData: [],
|
|
291
299
|
hasPersonalizationTokenError: false,
|
|
300
|
+
primarySaveButtonMessage: undefined,
|
|
292
301
|
};
|
|
293
302
|
export default SlideBoxFooter;
|
|
@@ -16,6 +16,7 @@ import { isTraiDLTEnable } from '../../utils/common';
|
|
|
16
16
|
import { formatString } from '../../utils/Formatter';
|
|
17
17
|
import {
|
|
18
18
|
CAP_SPACE_12,
|
|
19
|
+
CAP_SPACE_16,
|
|
19
20
|
} from '@capillarytech/cap-ui-library/styled/variables';
|
|
20
21
|
import { WHATSAPP_HELP_DOC_LINK, JOURNEY } from './constants';
|
|
21
22
|
|
|
@@ -24,7 +25,7 @@ const StyledLabel = styled(CapLabelInline)`
|
|
|
24
25
|
margin-right: ${CAP_SPACE_12};
|
|
25
26
|
`;
|
|
26
27
|
const PrefixWrapper = styled.div`
|
|
27
|
-
margin-right:
|
|
28
|
+
margin-right: ${CAP_SPACE_16};
|
|
28
29
|
`;
|
|
29
30
|
const renderData = (type, value, channel) => (
|
|
30
31
|
<StyledLabel className={channel?.toLowerCase() === ZALO ? 'zalo-template-name-spacing' : ''} type={type}>
|
|
@@ -33,7 +34,25 @@ const renderData = (type, value, channel) => (
|
|
|
33
34
|
);
|
|
34
35
|
|
|
35
36
|
export function SlideBoxHeader(props) {
|
|
36
|
-
const {
|
|
37
|
+
const {
|
|
38
|
+
slidBoxContent,
|
|
39
|
+
templateData,
|
|
40
|
+
onShowTemplates,
|
|
41
|
+
creativesMode,
|
|
42
|
+
isFullMode,
|
|
43
|
+
showPrefix,
|
|
44
|
+
shouldShowTemplateName,
|
|
45
|
+
channel,
|
|
46
|
+
templateNameRenderProp,
|
|
47
|
+
weChatTemplateType,
|
|
48
|
+
onWeChatMaptemplateStepChange,
|
|
49
|
+
weChatMaptemplateStep,
|
|
50
|
+
templateStep,
|
|
51
|
+
smsRegister,
|
|
52
|
+
handleClose,
|
|
53
|
+
moduleType,
|
|
54
|
+
useLocalTemplates = false,
|
|
55
|
+
} = props;
|
|
37
56
|
const showTemplateNameHeader = isFullMode && shouldShowTemplateName;
|
|
38
57
|
const mapTemplateCreate = !showTemplateNameHeader && slidBoxContent === 'createTemplate' && weChatTemplateType === MAP_TEMPLATE && templateStep !== 'modeSelection';
|
|
39
58
|
const isTraiDlt = isTraiDLTEnable(isFullMode, smsRegister);
|
|
@@ -81,6 +100,9 @@ export function SlideBoxHeader(props) {
|
|
|
81
100
|
window.open(WHATSAPP_HELP_DOC_LINK, '_blank');
|
|
82
101
|
};
|
|
83
102
|
|
|
103
|
+
const showCreativesTemplatesBackButton =
|
|
104
|
+
!isFullMode && (moduleType === JOURNEY || useLocalTemplates);
|
|
105
|
+
|
|
84
106
|
return (
|
|
85
107
|
<div key="creatives-container-slidebox-header-content">
|
|
86
108
|
{slidBoxContent === 'templates' && !showTemplateNameHeader && (
|
|
@@ -89,7 +111,7 @@ export function SlideBoxHeader(props) {
|
|
|
89
111
|
description={![NO_COMMUNICATION, FTP].includes(channel) &&
|
|
90
112
|
<FormattedMessage {...messages.creativeTemplatesDesc} />
|
|
91
113
|
}
|
|
92
|
-
prefix={
|
|
114
|
+
prefix={showCreativesTemplatesBackButton &&
|
|
93
115
|
<PrefixWrapper>
|
|
94
116
|
<CapIcons.backIcon onClick={handleClose} />
|
|
95
117
|
</PrefixWrapper>
|
|
@@ -135,7 +157,7 @@ export function SlideBoxHeader(props) {
|
|
|
135
157
|
}
|
|
136
158
|
</>
|
|
137
159
|
}
|
|
138
|
-
prefix={
|
|
160
|
+
prefix={!isFullMode && showPrefix &&
|
|
139
161
|
<PrefixWrapper>
|
|
140
162
|
<CapIcons.backIcon onClick={onShowTemplates} />
|
|
141
163
|
</PrefixWrapper>
|
|
@@ -191,5 +213,8 @@ SlideBoxHeader.propTypes = {
|
|
|
191
213
|
shouldShowTemplateName: PropTypes.bool,
|
|
192
214
|
templateNameRenderProp: PropTypes.func,
|
|
193
215
|
smsRegister: PropTypes.any,
|
|
216
|
+
handleClose: PropTypes.func,
|
|
217
|
+
moduleType: PropTypes.string,
|
|
218
|
+
useLocalTemplates: PropTypes.bool,
|
|
194
219
|
};
|
|
195
220
|
export default SlideBoxHeader;
|
|
@@ -67,3 +67,12 @@ export const ALLOWED_CHANNELS_FOR_ANONYMOUS = ['mobilepush', 'webpush'];
|
|
|
67
67
|
export const ALL_CHANNELS_NEW = [
|
|
68
68
|
'sms', 'email', 'whatsapp', 'facebook', 'line', 'viber', 'rcs', 'zalo', 'inapp', 'call_task', 'ftp',
|
|
69
69
|
];
|
|
70
|
+
|
|
71
|
+
export const LOCAL_TEMPLATE_CONFIG_KEYS = ['useLocalTemplates', 'localTemplates', 'localTemplatesLoading', 'localTemplatesFilterContent', 'localTemplatesUseSkeleton', 'localTemplatesOnPageChange'];
|
|
72
|
+
|
|
73
|
+
/** Keys passed from parents into `Templates` when using local SMS template list (extends `LOCAL_TEMPLATE_CONFIG_KEYS`). */
|
|
74
|
+
export const LOCAL_TEMPLATE_CONFIG_KEYS_FOR_PICK = [
|
|
75
|
+
...LOCAL_TEMPLATE_CONFIG_KEYS,
|
|
76
|
+
'localTemplatesLoadingTip',
|
|
77
|
+
'localTemplatesFooterContent',
|
|
78
|
+
];
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared logic for CreativesContainer slidebox + embedded flows (e.g. RCS SMS fallback)
|
|
3
|
+
* that mirror the same footer liquid errors and layout margins.
|
|
4
|
+
*/
|
|
5
|
+
import get from 'lodash/get';
|
|
6
|
+
import {
|
|
7
|
+
CAP_SPACE_32,
|
|
8
|
+
CAP_SPACE_56,
|
|
9
|
+
CAP_SPACE_64,
|
|
10
|
+
} from '@capillarytech/cap-ui-library/styled/variables';
|
|
11
|
+
import * as constants from './constants';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Returns true if value is "deep empty": no errors present.
|
|
15
|
+
* Same rules as CreativesContainer (used for liquid / standard error payloads).
|
|
16
|
+
*/
|
|
17
|
+
export function isDeepEmpty(value) {
|
|
18
|
+
if (value == null) return true;
|
|
19
|
+
if (typeof value === 'string') return value.length === 0;
|
|
20
|
+
if (Array.isArray(value)) return value.length === 0;
|
|
21
|
+
if (typeof value === 'object') {
|
|
22
|
+
return Object.values(value).every(isDeepEmpty);
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Header/content margin below slidebox chrome when ErrorInfoNote stacks errors — same formula as CreativesContainer#render.
|
|
29
|
+
*/
|
|
30
|
+
export function getSlideBoxWrapperMarginFromLiquidErrors(liquidErrorMessage) {
|
|
31
|
+
return (get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0
|
|
32
|
+
&& get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0)
|
|
33
|
+
? CAP_SPACE_64
|
|
34
|
+
: get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0
|
|
35
|
+
? CAP_SPACE_56
|
|
36
|
+
: get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0
|
|
37
|
+
? CAP_SPACE_32
|
|
38
|
+
: 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Maps FormBuilder `showLiquidErrorInFooter` args to slidebox footer state.
|
|
43
|
+
* Returns `null` when CreativesContainer intentionally skips updating (Mobile Push OLD empty clear).
|
|
44
|
+
*/
|
|
45
|
+
export function computeLiquidFooterUpdateFromFormBuilder(
|
|
46
|
+
errorMessagesFromFormBuilder,
|
|
47
|
+
currentFormBuilderTab,
|
|
48
|
+
{ previousIsLiquidValidationError, currentChannelUpper } = {},
|
|
49
|
+
) {
|
|
50
|
+
const liquidMsgs = get(errorMessagesFromFormBuilder, constants.LIQUID_ERROR_MSG, []);
|
|
51
|
+
const standardMsgs = get(errorMessagesFromFormBuilder, constants.STANDARD_ERROR_MSG, []);
|
|
52
|
+
const hasLiquid = !isDeepEmpty(liquidMsgs);
|
|
53
|
+
const hasStandard = !isDeepEmpty(standardMsgs);
|
|
54
|
+
const isLiquidValidationError = hasLiquid || hasStandard;
|
|
55
|
+
const isMobilePush = currentChannelUpper === constants.MOBILE_PUSH;
|
|
56
|
+
if (!hasLiquid && !hasStandard && previousIsLiquidValidationError && isMobilePush) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
isLiquidValidationError,
|
|
61
|
+
liquidErrorMessage: errorMessagesFromFormBuilder,
|
|
62
|
+
activeFormBuilderTab:
|
|
63
|
+
currentFormBuilderTab === 1
|
|
64
|
+
? constants.ANDROID
|
|
65
|
+
: (currentFormBuilderTab === 2 ? constants.IOS : null),
|
|
66
|
+
};
|
|
67
|
+
}
|