@capillarytech/creatives-library 8.0.345-alpha.15 → 8.0.345-alpha.17
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/package.json +1 -1
- package/utils/templateVarUtils.js +1 -1
- package/utils/tests/rcsPayloadUtils.test.js +226 -0
- package/v2Components/CommonTestAndPreview/messages.js +0 -4
- package/v2Components/SmsFallback/smsFallbackUtils.js +1 -1
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +1 -1
- package/v2Containers/CreativesContainer/index.js +7 -6
- package/v2Containers/Rcs/constants.js +0 -2
- package/v2Containers/Rcs/index.js +87 -18
- package/v2Containers/Rcs/messages.js +0 -4
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js.rej +0 -18
- package/v2Containers/Rcs/index.js.rej +0 -1336
- package/v2Containers/Rcs/index.scss.rej +0 -74
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap.rej +0 -128
package/package.json
CHANGED
|
@@ -82,7 +82,7 @@ export const splitTemplateVarString = (str = '', varRegex = DEFAULT_MUSTACHE_VAR
|
|
|
82
82
|
if (!str) return [];
|
|
83
83
|
const matchedVariableTokens = str.match(varRegex) || [];
|
|
84
84
|
return splitContentByOrderedVarTokens(matchedVariableTokens, str).filter(
|
|
85
|
-
(segment) => segment
|
|
85
|
+
(segment) => segment !== ''
|
|
86
86
|
);
|
|
87
87
|
};
|
|
88
88
|
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { normalizeRcsMessageContentForApi } from '../rcsPayloadUtils';
|
|
2
|
+
|
|
3
|
+
describe('normalizeRcsMessageContentForApi', () => {
|
|
4
|
+
it('defaults missing rcsContent to empty object and still normalizes root smsFallBackContent', () => {
|
|
5
|
+
const item = {
|
|
6
|
+
smsFallBackContent: { message: 'root-only' },
|
|
7
|
+
};
|
|
8
|
+
normalizeRcsMessageContentForApi(item);
|
|
9
|
+
expect(item.rcsContent).toEqual({});
|
|
10
|
+
expect(item.smsFallBackContent).toEqual({ message: 'root-only' });
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('moves nested smsFallBackContent out of rcsContent and merges onto root', () => {
|
|
14
|
+
const item = {
|
|
15
|
+
rcsContent: {
|
|
16
|
+
contentType: 'RICHCARD',
|
|
17
|
+
smsFallBackContent: {
|
|
18
|
+
message: 'nested-msg',
|
|
19
|
+
templateConfigs: { registeredSenderIds: ['H1'] },
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
smsFallBackContent: { message: 'root-msg' },
|
|
23
|
+
};
|
|
24
|
+
normalizeRcsMessageContentForApi(item);
|
|
25
|
+
expect(item.rcsContent).toEqual({ contentType: 'RICHCARD' });
|
|
26
|
+
expect(item.smsFallBackContent.message).toBe('root-msg');
|
|
27
|
+
expect(item.smsFallBackContent.templateConfigs).toEqual({
|
|
28
|
+
registeredSenderIds: ['H1'],
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('nested message merges when root has no message key', () => {
|
|
33
|
+
const item = {
|
|
34
|
+
rcsContent: {
|
|
35
|
+
smsFallBackContent: { message: 'from-nested' },
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
normalizeRcsMessageContentForApi(item);
|
|
39
|
+
expect(item.rcsContent).toEqual({});
|
|
40
|
+
expect(item.smsFallBackContent).toEqual({ message: 'from-nested' });
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('removes smsFallBackContent when nothing to serialize', () => {
|
|
44
|
+
const item = {
|
|
45
|
+
rcsContent: { smsFallBackContent: {} },
|
|
46
|
+
smsFallBackContent: {},
|
|
47
|
+
};
|
|
48
|
+
normalizeRcsMessageContentForApi(item);
|
|
49
|
+
expect(item.rcsContent).toEqual({});
|
|
50
|
+
expect(item.smsFallBackContent).toBeUndefined();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('does not add templateConfigs when empty', () => {
|
|
54
|
+
const item = {
|
|
55
|
+
rcsContent: {},
|
|
56
|
+
smsFallBackContent: { message: 'x', templateConfigs: {} },
|
|
57
|
+
};
|
|
58
|
+
normalizeRcsMessageContentForApi(item);
|
|
59
|
+
expect(item.smsFallBackContent).toEqual({ message: 'x' });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('serializes templateConfigs only when message is absent but templateConfigs has data', () => {
|
|
63
|
+
const item = {
|
|
64
|
+
rcsContent: {
|
|
65
|
+
smsFallBackContent: {
|
|
66
|
+
templateConfigs: { registeredSenderIds: ['Z'] },
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
normalizeRcsMessageContentForApi(item);
|
|
71
|
+
expect(item.smsFallBackContent).toEqual({
|
|
72
|
+
templateConfigs: { registeredSenderIds: ['Z'] },
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('includes message in api payload when key exists with empty string', () => {
|
|
77
|
+
const item = {
|
|
78
|
+
smsFallBackContent: { message: '', templateConfigs: { id: 't1' } },
|
|
79
|
+
};
|
|
80
|
+
normalizeRcsMessageContentForApi(item);
|
|
81
|
+
expect(item.smsFallBackContent).toEqual({
|
|
82
|
+
message: '',
|
|
83
|
+
templateConfigs: { id: 't1' },
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('merges when legacy nested is null-coalesced', () => {
|
|
88
|
+
const item = {
|
|
89
|
+
rcsContent: {
|
|
90
|
+
contentType: 'TEXT',
|
|
91
|
+
smsFallBackContent: null,
|
|
92
|
+
},
|
|
93
|
+
smsFallBackContent: { message: 'from-root' },
|
|
94
|
+
};
|
|
95
|
+
normalizeRcsMessageContentForApi(item);
|
|
96
|
+
expect(item.rcsContent).toEqual({ contentType: 'TEXT' });
|
|
97
|
+
expect(item.smsFallBackContent).toEqual({ message: 'from-root' });
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('folds rcsSmsFallbackVarMapped into message and omits var map from API shape', () => {
|
|
101
|
+
const msg = '{{optout}} {{fullname}} test SMS';
|
|
102
|
+
const varMapped = {
|
|
103
|
+
'{{optout}}_0': '{{city}}',
|
|
104
|
+
'{{fullname}}_2': '{{fullname}}',
|
|
105
|
+
};
|
|
106
|
+
const item = {
|
|
107
|
+
rcsContent: { contentType: 'RICHCARD' },
|
|
108
|
+
smsFallBackContent: {
|
|
109
|
+
message: msg,
|
|
110
|
+
rcsSmsFallbackVarMapped: varMapped,
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
normalizeRcsMessageContentForApi(item);
|
|
114
|
+
expect(item.smsFallBackContent).toEqual({
|
|
115
|
+
message: '{{city}} {{fullname}} test SMS',
|
|
116
|
+
});
|
|
117
|
+
expect(item.smsFallBackContent.rcsSmsFallbackVarMapped).toBeUndefined();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('merges nested rcsSmsFallbackVarMapped with root message; root message wins on key clash', () => {
|
|
121
|
+
const nestedVar = { '{{a}}_0': '{{nested}}' };
|
|
122
|
+
const item = {
|
|
123
|
+
rcsContent: {
|
|
124
|
+
contentType: 'RICHCARD',
|
|
125
|
+
smsFallBackContent: {
|
|
126
|
+
message: '{{a}} tail',
|
|
127
|
+
rcsSmsFallbackVarMapped: nestedVar,
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
smsFallBackContent: {
|
|
131
|
+
message: '{{a}} tail',
|
|
132
|
+
rcsSmsFallbackVarMapped: { '{{a}}_0': '{{root}}' },
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
normalizeRcsMessageContentForApi(item);
|
|
136
|
+
expect(item.smsFallBackContent.message).toBe('{{root}} tail');
|
|
137
|
+
expect(item.smsFallBackContent.rcsSmsFallbackVarMapped).toBeUndefined();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('non-slot var map keys do not change message but var map is still stripped', () => {
|
|
141
|
+
const varMapped = { 0: { label: 'City', tag: '{{city}}' } };
|
|
142
|
+
const item = {
|
|
143
|
+
rcsContent: { contentType: 'RICHCARD' },
|
|
144
|
+
smsFallBackContent: {
|
|
145
|
+
message: 'Hello',
|
|
146
|
+
rcsSmsFallbackVarMapped: varMapped,
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
normalizeRcsMessageContentForApi(item);
|
|
150
|
+
expect(item.smsFallBackContent).toEqual({ message: 'Hello' });
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('omits rcsSmsFallbackVarMapped when merged value is empty object', () => {
|
|
154
|
+
const item = {
|
|
155
|
+
rcsContent: {
|
|
156
|
+
smsFallBackContent: { rcsSmsFallbackVarMapped: {} },
|
|
157
|
+
},
|
|
158
|
+
smsFallBackContent: { message: 'only-msg' },
|
|
159
|
+
};
|
|
160
|
+
normalizeRcsMessageContentForApi(item);
|
|
161
|
+
expect(item.smsFallBackContent).toEqual({ message: 'only-msg' });
|
|
162
|
+
expect(item.smsFallBackContent.rcsSmsFallbackVarMapped).toBeUndefined();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('removes root-level templateConfigs from RCS message content item', () => {
|
|
166
|
+
const item = {
|
|
167
|
+
rcsContent: { cardType: 'STANDALONE' },
|
|
168
|
+
templateConfigs: {
|
|
169
|
+
templateId: '69b3d33934a7bd0db5bada8e',
|
|
170
|
+
template: '{{registered_store_name}} Its a test message',
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
normalizeRcsMessageContentForApi(item);
|
|
174
|
+
expect(item.templateConfigs).toBeUndefined();
|
|
175
|
+
expect(item.rcsContent).toEqual({ cardType: 'STANDALONE' });
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('removes templateConfigs nested inside rcsContent', () => {
|
|
179
|
+
const item = {
|
|
180
|
+
rcsContent: {
|
|
181
|
+
cardType: 'STANDALONE',
|
|
182
|
+
templateConfigs: {
|
|
183
|
+
templateId: '69b3d33934a7bd0db5bada8e',
|
|
184
|
+
template: '{{registered_store_name}} Its a test message',
|
|
185
|
+
},
|
|
186
|
+
cardContent: [{ title: 'Card' }],
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
normalizeRcsMessageContentForApi(item);
|
|
190
|
+
expect(item.templateConfigs).toBeUndefined();
|
|
191
|
+
expect(item.rcsContent.templateConfigs).toBeUndefined();
|
|
192
|
+
expect(item.rcsContent.cardContent).toEqual([{ title: 'Card' }]);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('strips isSaved from each suggestion in cardContent', () => {
|
|
196
|
+
const item = {
|
|
197
|
+
rcsContent: {
|
|
198
|
+
cardContent: [
|
|
199
|
+
{
|
|
200
|
+
title: 'Card 1',
|
|
201
|
+
suggestions: [
|
|
202
|
+
{ type: 'QUICK_REPLY', text: 'Yes', isSaved: true },
|
|
203
|
+
{ type: 'CTA', text: 'Go', url: 'https://example.com', isSaved: false },
|
|
204
|
+
],
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
normalizeRcsMessageContentForApi(item);
|
|
210
|
+
expect(item.rcsContent.cardContent[0].suggestions).toEqual([
|
|
211
|
+
{ type: 'QUICK_REPLY', text: 'Yes' },
|
|
212
|
+
{ type: 'CTA', text: 'Go', url: 'https://example.com' },
|
|
213
|
+
]);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('handles cardContent with no suggestions gracefully', () => {
|
|
217
|
+
const item = {
|
|
218
|
+
rcsContent: {
|
|
219
|
+
cardContent: [{ title: 'Card 1' }, { title: 'Card 2', suggestions: [] }],
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
normalizeRcsMessageContentForApi(item);
|
|
223
|
+
expect(item.rcsContent.cardContent[0].suggestions).toBeUndefined();
|
|
224
|
+
expect(item.rcsContent.cardContent[1].suggestions).toEqual([]);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
@@ -324,10 +324,6 @@ export default defineMessages({
|
|
|
324
324
|
id: `${scope}.senderId`,
|
|
325
325
|
defaultMessage: 'Sender ID',
|
|
326
326
|
},
|
|
327
|
-
rcsSenderIdLabel: {
|
|
328
|
-
id: `${scope}.rcsSenderIdLabel`,
|
|
329
|
-
defaultMessage: 'RCS sender ID',
|
|
330
|
-
},
|
|
331
327
|
domainLabel: {
|
|
332
328
|
id: `${scope}.domainLabel`,
|
|
333
329
|
defaultMessage: 'Domain',
|
|
@@ -13,13 +13,13 @@ describe('smsFallbackUtils', () => {
|
|
|
13
13
|
describe('buildFallbackDataFromTemplate', () => {
|
|
14
14
|
it('maps template versions.base and header sender list', () => {
|
|
15
15
|
const template = {
|
|
16
|
-
_id: 'tid',
|
|
17
16
|
name: 'My SMS',
|
|
18
17
|
versions: {
|
|
19
18
|
base: {
|
|
20
19
|
'sms-editor': 'Hello {{1}}',
|
|
21
20
|
header: ['S1', 'S2'],
|
|
22
21
|
'unicode-validity': false,
|
|
22
|
+
template_id: 'tid',
|
|
23
23
|
},
|
|
24
24
|
},
|
|
25
25
|
};
|
|
@@ -746,6 +746,7 @@ export class Creatives extends React.Component {
|
|
|
746
746
|
creativeName = "",
|
|
747
747
|
channel = constants.RCS,
|
|
748
748
|
rcsCardVarMapped,
|
|
749
|
+
accountId = "",
|
|
749
750
|
} = templateData || {};
|
|
750
751
|
const { isFullMode: isFullModeForRcsPayload } = this.props;
|
|
751
752
|
const firstCardIn = (rcsContent.cardContent && rcsContent.cardContent[0]) || {};
|
|
@@ -791,6 +792,7 @@ export class Creatives extends React.Component {
|
|
|
791
792
|
RCS: {
|
|
792
793
|
rcsContent: {
|
|
793
794
|
...rcsContent,
|
|
795
|
+
...(accountId && !isFullMode && { accountId }),
|
|
794
796
|
cardContent: [
|
|
795
797
|
{
|
|
796
798
|
...cardContent,
|
|
@@ -1242,7 +1244,7 @@ export class Creatives extends React.Component {
|
|
|
1242
1244
|
};
|
|
1243
1245
|
}
|
|
1244
1246
|
break;
|
|
1245
|
-
case constants.FACEBOOK:
|
|
1247
|
+
case constants.FACEBOOK:
|
|
1246
1248
|
if (template.value) {
|
|
1247
1249
|
const FacebookAd = template?.value?.versions?.base?.content?.FacebookAd;
|
|
1248
1250
|
const { type } = FacebookAd[0];
|
|
@@ -1286,9 +1288,8 @@ export class Creatives extends React.Component {
|
|
|
1286
1288
|
selectedMarketingObjective: template.value.selectedMarketingObjective,
|
|
1287
1289
|
};
|
|
1288
1290
|
}
|
|
1289
|
-
}
|
|
1290
1291
|
break;
|
|
1291
|
-
case constants.RCS:
|
|
1292
|
+
case constants.RCS:
|
|
1292
1293
|
if (template.value) {
|
|
1293
1294
|
const { isFullMode: isFullModeForRcsConsumerPayload } = this.props;
|
|
1294
1295
|
const { name = "", versions = {} } = template.value || {};
|
|
@@ -1336,6 +1337,7 @@ export class Creatives extends React.Component {
|
|
|
1336
1337
|
contentType = "",
|
|
1337
1338
|
cardType = "",
|
|
1338
1339
|
cardSettings = {},
|
|
1340
|
+
accountId = "",
|
|
1339
1341
|
} = get(versions, 'base.content.RCS.rcsContent', {});
|
|
1340
1342
|
const rootRcsCardVarMappedFromSubmit = get(template, 'value.rcsCardVarMapped');
|
|
1341
1343
|
const firstCardFromSubmit = Array.isArray(cardContentFromSubmit)
|
|
@@ -1365,6 +1367,7 @@ export class Creatives extends React.Component {
|
|
|
1365
1367
|
channel,
|
|
1366
1368
|
creativeName: name,
|
|
1367
1369
|
rcsContent,
|
|
1370
|
+
accountId,
|
|
1368
1371
|
...(includeRootRcsCardVarMappedOnConsumerPayload
|
|
1369
1372
|
? { rcsCardVarMapped: cardVarMappedFromFirstRcsCard }
|
|
1370
1373
|
: {}),
|
|
@@ -1387,9 +1390,8 @@ export class Creatives extends React.Component {
|
|
|
1387
1390
|
}
|
|
1388
1391
|
normalizeRcsMessageContentForApi(templateData);
|
|
1389
1392
|
}
|
|
1390
|
-
}
|
|
1391
1393
|
break;
|
|
1392
|
-
case constants.ZALO:
|
|
1394
|
+
case constants.ZALO:
|
|
1393
1395
|
if (template.value) {
|
|
1394
1396
|
templateData = {
|
|
1395
1397
|
...template.value,
|
|
@@ -1398,7 +1400,6 @@ export class Creatives extends React.Component {
|
|
|
1398
1400
|
delete templateData.type;
|
|
1399
1401
|
}
|
|
1400
1402
|
}
|
|
1401
|
-
}
|
|
1402
1403
|
break;
|
|
1403
1404
|
case constants.WEBPUSH: {
|
|
1404
1405
|
if (template.value) {
|
|
@@ -34,6 +34,13 @@ import CapCheckbox from '@capillarytech/cap-ui-library/CapCheckbox';
|
|
|
34
34
|
import CapAskAira from '@capillarytech/cap-ui-library/CapAskAira';
|
|
35
35
|
import CapLink from '@capillarytech/cap-ui-library/CapLink';
|
|
36
36
|
import CapTab from '@capillarytech/cap-ui-library/CapTab';
|
|
37
|
+
import { flushSync } from 'react-dom';
|
|
38
|
+
import { isUrl, isValidText } from '../Line/Container/Wrapper/utils';
|
|
39
|
+
import {
|
|
40
|
+
invalidVarRegex,
|
|
41
|
+
RCS_CTA_URL_TYPE,
|
|
42
|
+
URL_MAX_LENGTH,
|
|
43
|
+
} from '../../v2Components/CapActionButton/constants';
|
|
37
44
|
|
|
38
45
|
import {
|
|
39
46
|
CAP_G01,
|
|
@@ -352,6 +359,7 @@ export const Rcs = (props) => {
|
|
|
352
359
|
useEffect(() => {
|
|
353
360
|
setSuggestions(isHostIcs ? INITIAL_SUGGESTIONS_DATA_STOP : []);
|
|
354
361
|
}, [isHostIcs]);
|
|
362
|
+
const [rcsAccount, setRcsAccount] = useState('');
|
|
355
363
|
useEffect(() => {
|
|
356
364
|
const accountObj = accountData.selectedRcsAccount || {};
|
|
357
365
|
if (!isEmpty(accountObj)) {
|
|
@@ -367,6 +375,7 @@ export const Rcs = (props) => {
|
|
|
367
375
|
setAccessToken(configs.accessToken || '');
|
|
368
376
|
setHostName(accountObj.hostName || '');
|
|
369
377
|
setAccountName(accountObj.name || '');
|
|
378
|
+
setRcsAccount(accountObj.id || '');
|
|
370
379
|
} else {
|
|
371
380
|
setAccountId('');
|
|
372
381
|
setWecrmAccountId('');
|
|
@@ -1700,6 +1709,10 @@ export const Rcs = (props) => {
|
|
|
1700
1709
|
get(details, 'versions.base.content.RCS.rcsContent.cardSettings', '')
|
|
1701
1710
|
|| get(details, 'rcsContent.cardSettings', '');
|
|
1702
1711
|
setMediaData(mediaData, mediaType, cardSettings);
|
|
1712
|
+
if (details?.edit) {
|
|
1713
|
+
const rcsAccountId = get(details, 'versions.base.content.RCS.rcsContent.accountId', '');
|
|
1714
|
+
setRcsAccount(rcsAccountId);
|
|
1715
|
+
}
|
|
1703
1716
|
|
|
1704
1717
|
const smsFallbackContent = mergeRcsSmsFallBackContentFromDetails(details);
|
|
1705
1718
|
const base = get(smsFallbackContent, 'versions.base', {});
|
|
@@ -1963,8 +1976,8 @@ export const Rcs = (props) => {
|
|
|
1963
1976
|
const onTagSelectFallback = (data) => {
|
|
1964
1977
|
const tempMsg = `${fallbackMessage}{{${data}}}`;
|
|
1965
1978
|
const error = fallbackMessageErrorHandler(tempMsg);
|
|
1966
|
-
setFallbackMessage(tempMsg);
|
|
1967
|
-
setFallbackMessageError(error);
|
|
1979
|
+
// setFallbackMessage(tempMsg);
|
|
1980
|
+
// setFallbackMessageError(error);
|
|
1968
1981
|
};
|
|
1969
1982
|
|
|
1970
1983
|
|
|
@@ -2136,8 +2149,8 @@ export const Rcs = (props) => {
|
|
|
2136
2149
|
|
|
2137
2150
|
const onFallbackMessageChange = ({ target: { value } }) => {
|
|
2138
2151
|
const error = fallbackMessageErrorHandler(value);
|
|
2139
|
-
setFallbackMessage(value);
|
|
2140
|
-
setFallbackMessageError(error);
|
|
2152
|
+
// setFallbackMessage(value);
|
|
2153
|
+
// setFallbackMessageError(error);
|
|
2141
2154
|
};
|
|
2142
2155
|
|
|
2143
2156
|
const fallbackMessageErrorHandler = (value) => {
|
|
@@ -2714,8 +2727,8 @@ const onTitleAddVar = () => {
|
|
|
2714
2727
|
const rcsDltCardDeleteHandler = () => {
|
|
2715
2728
|
closeDltContainerHandler();
|
|
2716
2729
|
setDltEditData({});
|
|
2717
|
-
setFallbackMessage('');
|
|
2718
|
-
setFallbackMessageError(false);
|
|
2730
|
+
// setFallbackMessage('');
|
|
2731
|
+
// setFallbackMessageError(false);
|
|
2719
2732
|
setShowDltCard(false);
|
|
2720
2733
|
};
|
|
2721
2734
|
|
|
@@ -3295,6 +3308,36 @@ const onTitleAddVar = () => {
|
|
|
3295
3308
|
return hasMeaningfulSmsFallbackShape(mapped) ? mapped : null;
|
|
3296
3309
|
})();
|
|
3297
3310
|
|
|
3311
|
+
const carouselCardContent = isCarouselType
|
|
3312
|
+
? (carouselData || []).map((card = {}) => {
|
|
3313
|
+
const cardMediaType = card.mediaType;
|
|
3314
|
+
const isCardVideo = cardMediaType === RCS_MEDIA_TYPES.VIDEO;
|
|
3315
|
+
const isCardImage = cardMediaType === RCS_MEDIA_TYPES.IMAGE;
|
|
3316
|
+
const mediaUrl = isCardVideo
|
|
3317
|
+
? (card.videoAsset?.videoSrc || '')
|
|
3318
|
+
: (card.imageSrc || '');
|
|
3319
|
+
const thumbnailUrl = isCardVideo
|
|
3320
|
+
? (card.thumbnailSrc || card.videoAsset?.videoThumbnail || '')
|
|
3321
|
+
: '';
|
|
3322
|
+
return {
|
|
3323
|
+
title: card.title || '',
|
|
3324
|
+
description: card.description || '',
|
|
3325
|
+
mediaType: cardMediaType,
|
|
3326
|
+
...((isCardImage || isCardVideo) && {
|
|
3327
|
+
media: {
|
|
3328
|
+
mediaUrl,
|
|
3329
|
+
thumbnailUrl,
|
|
3330
|
+
height: selectedCarouselHeight || MEDIUM,
|
|
3331
|
+
...(isCardVideo && card.videoAsset?.videoName && { videoName: card.videoAsset.videoName }),
|
|
3332
|
+
},
|
|
3333
|
+
}),
|
|
3334
|
+
...(Array.isArray(card.suggestions) && card.suggestions.length > 0 && {
|
|
3335
|
+
suggestions: card.suggestions,
|
|
3336
|
+
}),
|
|
3337
|
+
};
|
|
3338
|
+
})
|
|
3339
|
+
: null;
|
|
3340
|
+
|
|
3298
3341
|
const payload = {
|
|
3299
3342
|
name: templateName,
|
|
3300
3343
|
versions: {
|
|
@@ -3302,13 +3345,18 @@ const onTitleAddVar = () => {
|
|
|
3302
3345
|
content: {
|
|
3303
3346
|
RCS: {
|
|
3304
3347
|
rcsContent: {
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3348
|
+
...(rcsAccount && !isFullMode && { accountId: rcsAccount }),
|
|
3349
|
+
cardType: isCarouselType ? contentType.carousel : STANDALONE,
|
|
3350
|
+
cardSettings: isCarouselType
|
|
3351
|
+
? { cardWidth: selectedCarouselWidth || SMALL }
|
|
3352
|
+
: {
|
|
3353
|
+
cardOrientation: isMediaTypeImage ? RCS_IMAGE_DIMENSIONS[selectedDimension]?.orientation || VERTICAL : RCS_VIDEO_THUMBNAIL_DIMENSIONS[selectedDimension]?.orientation || VERTICAL,
|
|
3354
|
+
...(alignment && { mediaAlignment: alignment }),
|
|
3355
|
+
cardWidth: cardWidthFromSelection,
|
|
3356
|
+
},
|
|
3357
|
+
cardContent: isCarouselType
|
|
3358
|
+
? carouselCardContent
|
|
3359
|
+
: [
|
|
3312
3360
|
{
|
|
3313
3361
|
// Persist raw template copy + cardVarMapped — not resolveTemplateWithMap output — so library
|
|
3314
3362
|
// / getFormData round-trip keeps {{…}} and slot values (resolved strings broke reopen hydration).
|
|
@@ -3429,8 +3477,29 @@ const onTitleAddVar = () => {
|
|
|
3429
3477
|
}),
|
|
3430
3478
|
}
|
|
3431
3479
|
: {
|
|
3432
|
-
//
|
|
3480
|
+
// Round-trip storage: full shape so reopening the editor restores template
|
|
3481
|
+
// name, sender IDs, and var mappings. normalizeRcsMessageContentForApi
|
|
3482
|
+
// (called in CreativesContainer getCreativesData) strips this to
|
|
3483
|
+
// { message, templateConfigs } before the API call — the extra fields here
|
|
3484
|
+
// are only used for re-hydration, never sent to the API.
|
|
3433
3485
|
message: smsBodyText,
|
|
3486
|
+
...(smsFallbackForPayload.templateName && {
|
|
3487
|
+
smsTemplateName: smsFallbackForPayload.templateName,
|
|
3488
|
+
}),
|
|
3489
|
+
...(smsFallbackForPayload.templateContent && {
|
|
3490
|
+
templateContent: smsFallbackForPayload.templateContent,
|
|
3491
|
+
}),
|
|
3492
|
+
...(typeof smsFallbackForPayload.unicodeValidity === 'boolean' && {
|
|
3493
|
+
unicodeValidity: smsFallbackForPayload.unicodeValidity,
|
|
3494
|
+
}),
|
|
3495
|
+
...(Array.isArray(registeredSenderIdsForPayload) && {
|
|
3496
|
+
registeredSenderIds: registeredSenderIdsForPayload,
|
|
3497
|
+
}),
|
|
3498
|
+
...(smsFallbackForPayload[RCS_SMS_FALLBACK_VAR_MAPPED_PROP]
|
|
3499
|
+
&& Object.keys(smsFallbackForPayload[RCS_SMS_FALLBACK_VAR_MAPPED_PROP]).length > 0 && {
|
|
3500
|
+
[RCS_SMS_FALLBACK_VAR_MAPPED_PROP]:
|
|
3501
|
+
smsFallbackForPayload[RCS_SMS_FALLBACK_VAR_MAPPED_PROP],
|
|
3502
|
+
}),
|
|
3434
3503
|
...(isDltCampaign && smsFallbackTemplateConfigs && {
|
|
3435
3504
|
templateConfigs: smsFallbackTemplateConfigs,
|
|
3436
3505
|
}),
|
|
@@ -3757,18 +3826,18 @@ const onTitleAddVar = () => {
|
|
|
3757
3826
|
return true;
|
|
3758
3827
|
}
|
|
3759
3828
|
|
|
3760
|
-
if (isMediaTypeText && templateDesc.trim() === '') {
|
|
3829
|
+
if (!isCarouselType && isMediaTypeText && templateDesc.trim() === '') {
|
|
3761
3830
|
return true;
|
|
3762
3831
|
}
|
|
3763
|
-
if (isMediaTypeImage && rcsImageSrc === '') {
|
|
3832
|
+
if (!isCarouselType && isMediaTypeImage && rcsImageSrc === '') {
|
|
3764
3833
|
return true;
|
|
3765
3834
|
}
|
|
3766
|
-
if(isMediaTypeVideo && (rcsThumbnailSrc === '' || rcsVideoSrc.videoSrc === '')) {
|
|
3835
|
+
if (!isCarouselType && isMediaTypeVideo && (rcsThumbnailSrc === '' || rcsVideoSrc.videoSrc === '')) {
|
|
3767
3836
|
return true;
|
|
3768
3837
|
}
|
|
3769
3838
|
|
|
3770
3839
|
if (buttonType.includes(CTA)) {
|
|
3771
|
-
const hasValidButtons = suggestions.every(suggestion =>
|
|
3840
|
+
const hasValidButtons = suggestions.every(suggestion =>
|
|
3772
3841
|
suggestion.text && suggestion.url && !suggestionError && !forbiddenCharactersValidation(suggestion.text)
|
|
3773
3842
|
);
|
|
3774
3843
|
if (!hasValidButtons) {
|
|
@@ -47,10 +47,6 @@ export default defineMessages({
|
|
|
47
47
|
id: `${prefix}.btnLabel`,
|
|
48
48
|
defaultMessage: 'Buttons',
|
|
49
49
|
},
|
|
50
|
-
carouselMediaVideoOption:{
|
|
51
|
-
id: `${prefix}.carouselMediaVideoOption`,
|
|
52
|
-
defaultMessage: 'Video',
|
|
53
|
-
},
|
|
54
50
|
btnTypeNone: {
|
|
55
51
|
id: `${prefix}.btnTypeNone`,
|
|
56
52
|
defaultMessage: 'None',
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
diff a/app/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js b/app/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js (rejected hunks)
|
|
2
|
-
@@ -226,11 +361,16 @@ const RcsPreviewContent = ({
|
|
3
|
-
const timestamp = `${displayHours}:${displayMinutes} ${ampm}`;
|
|
4
|
-
|
|
5
|
-
// Render normal RCS preview (same structure as SMS up to sms-message-container)
|
|
6
|
-
+ const hasCarousel = Array.isArray(content?.carouselData) && content.carouselData.length > 0;
|
|
7
|
-
const hasMedia = !!(content?.rcsImageSrc || content?.rcsVideoSrc);
|
|
8
|
-
+ const contentToRender = hasCarousel
|
|
9
|
-
+ ? renderCarouselPreviewContent()
|
|
10
|
-
+ : (hasMedia ? renderMediaPreviewContent() : renderTextPreviewContent());
|
|
11
|
-
|
|
12
|
-
return (
|
|
13
|
-
+ <CapRow
|
|
14
|
-
+ className={`sms-device-container rcs-device-container ${hasCarousel ? 'rcs-device-container-carousel' : ''}`}
|
|
15
|
-
+ >
|
|
16
|
-
{/* Device Background Image */}
|
|
17
|
-
<CapImage
|
|
18
|
-
className="sms-device-image"
|