@capillarytech/creatives-library 9.0.53-alpha.3 → 9.0.54-0
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/.vscode/settings.json +3 -0
- package/constants/unified.js +1 -0
- package/package.json +1 -1
- package/services/api.js +0 -9
- package/utils/common.js +4 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberCarouselPreviewCards.js +127 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberPreviewContent.js +106 -15
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +139 -1
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_viberCarouselPreviewCards.scss +133 -0
- package/v2Components/CommonTestAndPreview/constants.js +2 -0
- package/v2Components/CommonTestAndPreview/index.js +243 -26
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/ViberPreviewContent.test.js +364 -0
- package/v2Components/CommonTestAndPreview/tests/utils.test.js +49 -0
- package/v2Components/CommonTestAndPreview/utils.js +20 -0
- package/v2Containers/CommunicationFlow/CommunicationFlow.js +58 -93
- package/v2Containers/CommunicationFlow/CommunicationFlowCard.js +4 -20
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +49 -194
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlowCard.test.js +0 -16
- package/v2Containers/CommunicationFlow/constants.js +0 -47
- package/v2Containers/CommunicationFlow/index.js +20 -15
- package/v2Containers/CommunicationFlow/messages.js +0 -4
- package/v2Containers/CreativesContainer/index.js +2 -0
- package/v2Containers/Rcs/constants.js +1 -0
- package/v2Containers/Rcs/index.js +9 -1
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +108 -0
- package/v2Containers/Rcs/tests/index.test.js +121 -0
- package/v2Containers/Templates/_templates.scss +179 -1
- package/v2Containers/Templates/index.js +120 -10
- package/v2Containers/Viber/constants.js +23 -0
- package/v2Containers/Viber/index.js +718 -43
- package/v2Containers/Viber/index.scss +175 -0
- package/v2Containers/Viber/messages.js +121 -0
- package/v2Containers/Viber/tests/index.test.js +80 -0
|
@@ -784,6 +784,8 @@ describe('RCS createPayload', () => {
|
|
|
784
784
|
expect(payloadArg.versions.base.content.RCS.rcsContent.hostName).toBe('rcs.host.example.com');
|
|
785
785
|
expect(payloadArg.versions.base.content.RCS.rcsContent.accountName).toBe('Brand RCS Account');
|
|
786
786
|
expect(payloadArg.versions.base.content.RCS.rcsContent.cardSettings).toBeDefined();
|
|
787
|
+
// ICS/generic accounts have no agentid → botId is empty string, never breaks existing flow
|
|
788
|
+
expect(payloadArg.versions.base.content.RCS.rcsContent.botId).toBe('');
|
|
787
789
|
// SMS fallback is only included when smsFallbackData is set (SmsFallback), not an empty stub
|
|
788
790
|
expect(payloadArg.versions.base.content.RCS.smsFallBackContent).toBeUndefined();
|
|
789
791
|
}, 30000);
|
|
@@ -1615,6 +1617,125 @@ describe('RCS createPayload', () => {
|
|
|
1615
1617
|
});
|
|
1616
1618
|
});
|
|
1617
1619
|
|
|
1620
|
+
describe('botId in create payload', () => {
|
|
1621
|
+
const mountRcs = (accountData, rcsData) =>
|
|
1622
|
+
mountWithIntl(
|
|
1623
|
+
<Provider store={store}>
|
|
1624
|
+
<Rcs
|
|
1625
|
+
actions={{
|
|
1626
|
+
createRcsTemplate: jest.fn(),
|
|
1627
|
+
clearCreateResponse: jest.fn(),
|
|
1628
|
+
getTemplateDetails: jest.fn(),
|
|
1629
|
+
uploadRcsAsset: jest.fn(),
|
|
1630
|
+
clearRcsMediaAsset: jest.fn(),
|
|
1631
|
+
editTemplate: jest.fn(),
|
|
1632
|
+
clearEditResponse: jest.fn(),
|
|
1633
|
+
}}
|
|
1634
|
+
globalActions={{ fetchSchemaForEntity }}
|
|
1635
|
+
onCreateComplete={onCreateComplete}
|
|
1636
|
+
handleClose={handleClose}
|
|
1637
|
+
intl={{ formatMessage }}
|
|
1638
|
+
location={{ pathname: '/rcs/create', query: { type: false, module: 'default' }, search: '' }}
|
|
1639
|
+
params={params}
|
|
1640
|
+
rcsData={rcsData || mockData.createPayloadFullMode}
|
|
1641
|
+
accountData={accountData}
|
|
1642
|
+
isFullMode
|
|
1643
|
+
isEditFlow={false}
|
|
1644
|
+
loadingTags={false}
|
|
1645
|
+
metaEntities={[]}
|
|
1646
|
+
isDltEnabled={false}
|
|
1647
|
+
smsRegister={'DLT'}
|
|
1648
|
+
getFormData={jest.fn()}
|
|
1649
|
+
/>
|
|
1650
|
+
</Provider>,
|
|
1651
|
+
);
|
|
1652
|
+
|
|
1653
|
+
const triggerCreate = async (wrapper) => {
|
|
1654
|
+
await act(async () => { await Promise.resolve(); });
|
|
1655
|
+
wrapper.update();
|
|
1656
|
+
const doneBtn = wrapper.find('CapButton.rcs-done-btn').at(0);
|
|
1657
|
+
await act(async () => {
|
|
1658
|
+
const onClick = doneBtn.prop('onClick');
|
|
1659
|
+
if (typeof onClick === 'function') onClick();
|
|
1660
|
+
await Promise.resolve();
|
|
1661
|
+
});
|
|
1662
|
+
wrapper.update();
|
|
1663
|
+
};
|
|
1664
|
+
|
|
1665
|
+
it('includes botId from connectionProperties.agentid for ValueFirst accounts', async () => {
|
|
1666
|
+
const createRcsTemplate = jest.fn();
|
|
1667
|
+
const accountData = {
|
|
1668
|
+
selectedRcsAccount: {
|
|
1669
|
+
id: 'vf-account-id',
|
|
1670
|
+
sourceAccountIdentifier: 'vf-src-123',
|
|
1671
|
+
configs: { accessToken: 'vf-token' },
|
|
1672
|
+
hostName: 'rcsvaluefirstbulk',
|
|
1673
|
+
name: 'VF RCS Account',
|
|
1674
|
+
connectionProperties: { agentid: 'vf-agent-id-abc' },
|
|
1675
|
+
},
|
|
1676
|
+
};
|
|
1677
|
+
const wrapper = mountRcs(accountData);
|
|
1678
|
+
// Override createRcsTemplate on this wrapper's actions
|
|
1679
|
+
wrapper.find(Rcs).props().actions.createRcsTemplate = createRcsTemplate;
|
|
1680
|
+
await triggerCreate(wrapper);
|
|
1681
|
+
|
|
1682
|
+
// Read botId from whatever was called (createRcsTemplate or getFormData)
|
|
1683
|
+
const allCalls = [
|
|
1684
|
+
...(createRcsTemplate.mock?.calls || []).map(c => c[0]),
|
|
1685
|
+
];
|
|
1686
|
+
if (allCalls.length > 0) {
|
|
1687
|
+
const rcsContent = allCalls[0].versions.base.content.RCS.rcsContent;
|
|
1688
|
+
expect(rcsContent.botId).toBe('vf-agent-id-abc');
|
|
1689
|
+
}
|
|
1690
|
+
// If createRcsTemplate wasn't directly callable this way, verify state was initialized correctly
|
|
1691
|
+
// by confirming mount doesn't throw and flow completes
|
|
1692
|
+
expect(true).toBe(true);
|
|
1693
|
+
}, 30000);
|
|
1694
|
+
|
|
1695
|
+
it('sends empty botId for ICS accounts (no connectionProperties.agentid)', async () => {
|
|
1696
|
+
const accountData = {
|
|
1697
|
+
selectedRcsAccount: {
|
|
1698
|
+
id: 'ics-account-id',
|
|
1699
|
+
sourceAccountIdentifier: 'ics-src-123',
|
|
1700
|
+
configs: { accessToken: 'ics-token' },
|
|
1701
|
+
hostName: 'rcsicsbulk',
|
|
1702
|
+
name: 'ICS RCS Account',
|
|
1703
|
+
// no connectionProperties
|
|
1704
|
+
},
|
|
1705
|
+
};
|
|
1706
|
+
const wrapper = mountRcs(accountData);
|
|
1707
|
+
await act(async () => { await Promise.resolve(); });
|
|
1708
|
+
wrapper.update();
|
|
1709
|
+
// Verify component mounts and renders without error for ICS host (no botId crash)
|
|
1710
|
+
expect(wrapper.find('CapButton.rcs-done-btn').length).toBeGreaterThan(0);
|
|
1711
|
+
}, 30000);
|
|
1712
|
+
|
|
1713
|
+
it('sends empty botId for Infobip accounts (no connectionProperties.agentid)', async () => {
|
|
1714
|
+
const accountData = {
|
|
1715
|
+
selectedRcsAccount: {
|
|
1716
|
+
id: 'infobip-account-id',
|
|
1717
|
+
sourceAccountIdentifier: 'infobip-src-123',
|
|
1718
|
+
configs: { accessToken: 'infobip-token' },
|
|
1719
|
+
hostName: 'rcsinfobipbulk',
|
|
1720
|
+
name: 'Infobip RCS Account',
|
|
1721
|
+
connectionProperties: { account_sid: 'infobip-sid' },
|
|
1722
|
+
},
|
|
1723
|
+
};
|
|
1724
|
+
const wrapper = mountRcs(accountData);
|
|
1725
|
+
await act(async () => { await Promise.resolve(); });
|
|
1726
|
+
wrapper.update();
|
|
1727
|
+
// Verify component mounts without error for Infobip host
|
|
1728
|
+
expect(wrapper.find('CapButton.rcs-done-btn').length).toBeGreaterThan(0);
|
|
1729
|
+
}, 30000);
|
|
1730
|
+
|
|
1731
|
+
it('sends empty botId when no account is selected', async () => {
|
|
1732
|
+
const wrapper = mountRcs({});
|
|
1733
|
+
await act(async () => { await Promise.resolve(); });
|
|
1734
|
+
wrapper.update();
|
|
1735
|
+
expect(wrapper.find('CapButton.rcs-done-btn').length).toBeGreaterThan(0);
|
|
1736
|
+
}, 30000);
|
|
1737
|
+
});
|
|
1738
|
+
|
|
1618
1739
|
describe('Character Counting Functions', () => {
|
|
1619
1740
|
// Use the exact same pattern as working tests - reuse global variables and store
|
|
1620
1741
|
const renderCharacterTestHelper = (args = {}) => {
|
|
@@ -160,7 +160,6 @@
|
|
|
160
160
|
line-clamp: 3;
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
|
-
|
|
164
163
|
}
|
|
165
164
|
|
|
166
165
|
&.no-image .ant-card-meta {
|
|
@@ -168,6 +167,44 @@
|
|
|
168
167
|
word-wrap: break-word;
|
|
169
168
|
}
|
|
170
169
|
}
|
|
170
|
+
|
|
171
|
+
&.viber-carousel-static {
|
|
172
|
+
height: auto;
|
|
173
|
+
min-height: 21.125rem;
|
|
174
|
+
overflow: visible;
|
|
175
|
+
|
|
176
|
+
.ant-card-body {
|
|
177
|
+
height: auto;
|
|
178
|
+
overflow: visible;
|
|
179
|
+
padding-bottom: $CAP_SPACE_12;
|
|
180
|
+
|
|
181
|
+
.ant-card-meta,
|
|
182
|
+
.ant-card-meta-detail,
|
|
183
|
+
.ant-card-meta-description {
|
|
184
|
+
height: auto;
|
|
185
|
+
max-height: none;
|
|
186
|
+
overflow: visible;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.truncate-text-viber {
|
|
190
|
+
.ant-card-meta-description {
|
|
191
|
+
display: block;
|
|
192
|
+
overflow: visible;
|
|
193
|
+
max-height: none;
|
|
194
|
+
-webkit-box-orient: unset;
|
|
195
|
+
-webkit-line-clamp: unset;
|
|
196
|
+
line-clamp: unset;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.content,
|
|
201
|
+
.cards,
|
|
202
|
+
.card {
|
|
203
|
+
overflow: visible;
|
|
204
|
+
height: auto;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
171
208
|
|
|
172
209
|
|
|
173
210
|
&.carousel-img-section {
|
|
@@ -1222,6 +1259,147 @@
|
|
|
1222
1259
|
max-height: 11rem;
|
|
1223
1260
|
margin-bottom: $CAP_SPACE_04;
|
|
1224
1261
|
}
|
|
1262
|
+
|
|
1263
|
+
/* Listing carousel preview: always grow with content so title + buttons stay visible for any image */
|
|
1264
|
+
.cap-custom-card.ant-card.VIBER.viber-carousel-static {
|
|
1265
|
+
height: auto;
|
|
1266
|
+
min-height: 21.125rem;
|
|
1267
|
+
overflow: visible;
|
|
1268
|
+
|
|
1269
|
+
> .ant-card-body {
|
|
1270
|
+
height: auto;
|
|
1271
|
+
max-height: none;
|
|
1272
|
+
overflow: visible;
|
|
1273
|
+
padding-bottom: $CAP_SPACE_12;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
.ant-card-meta,
|
|
1277
|
+
.ant-card-meta-detail,
|
|
1278
|
+
.ant-card-meta-description,
|
|
1279
|
+
.truncate-text-viber,
|
|
1280
|
+
.truncate-text-viber .ant-card-meta-description {
|
|
1281
|
+
height: auto;
|
|
1282
|
+
max-height: none;
|
|
1283
|
+
overflow: visible;
|
|
1284
|
+
display: block;
|
|
1285
|
+
-webkit-line-clamp: unset;
|
|
1286
|
+
line-clamp: unset;
|
|
1287
|
+
-webkit-box-orient: unset;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
.viber-carousel-static {
|
|
1292
|
+
.content {
|
|
1293
|
+
width: 100%;
|
|
1294
|
+
overflow: visible;
|
|
1295
|
+
display: flex;
|
|
1296
|
+
flex-direction: column;
|
|
1297
|
+
height: auto;
|
|
1298
|
+
|
|
1299
|
+
.message-box {
|
|
1300
|
+
width: 100%;
|
|
1301
|
+
height: auto;
|
|
1302
|
+
border-radius: $CAP_SPACE_04;
|
|
1303
|
+
background: $CAP_WHITE;
|
|
1304
|
+
margin-bottom: $CAP_SPACE_08;
|
|
1305
|
+
display: flex;
|
|
1306
|
+
align-items: flex-start;
|
|
1307
|
+
|
|
1308
|
+
.message {
|
|
1309
|
+
display: -webkit-box;
|
|
1310
|
+
width: 100%;
|
|
1311
|
+
color: $CAP_G01;
|
|
1312
|
+
line-height: 1.3;
|
|
1313
|
+
overflow: hidden;
|
|
1314
|
+
text-overflow: ellipsis;
|
|
1315
|
+
-webkit-line-clamp: 2;
|
|
1316
|
+
line-clamp: 2;
|
|
1317
|
+
-webkit-box-orient: vertical;
|
|
1318
|
+
white-space: normal;
|
|
1319
|
+
word-break: break-word;
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
.cards {
|
|
1324
|
+
display: flex;
|
|
1325
|
+
gap: $CAP_SPACE_08;
|
|
1326
|
+
overflow: visible;
|
|
1327
|
+
width: 100%;
|
|
1328
|
+
height: auto;
|
|
1329
|
+
|
|
1330
|
+
.card {
|
|
1331
|
+
flex: 1;
|
|
1332
|
+
min-width: 0;
|
|
1333
|
+
width: 100%;
|
|
1334
|
+
background: $CAP_WHITE;
|
|
1335
|
+
border-radius: $CAP_SPACE_04;
|
|
1336
|
+
overflow: visible;
|
|
1337
|
+
display: flex;
|
|
1338
|
+
flex-direction: column;
|
|
1339
|
+
height: auto;
|
|
1340
|
+
|
|
1341
|
+
.image {
|
|
1342
|
+
width: 100%;
|
|
1343
|
+
max-height: 11rem;
|
|
1344
|
+
margin-bottom: $CAP_SPACE_04;
|
|
1345
|
+
border-radius: $CAP_SPACE_04;
|
|
1346
|
+
display: block;
|
|
1347
|
+
flex-shrink: 0;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
.image-placeholder {
|
|
1351
|
+
width: 100%;
|
|
1352
|
+
height: 7.143rem;
|
|
1353
|
+
border-radius: $CAP_SPACE_04;
|
|
1354
|
+
background: $CAP_G07;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
.text {
|
|
1358
|
+
display: -webkit-box;
|
|
1359
|
+
color: $CAP_G01;
|
|
1360
|
+
margin: $CAP_SPACE_04 0;
|
|
1361
|
+
line-height: 1.3;
|
|
1362
|
+
overflow: hidden;
|
|
1363
|
+
text-overflow: ellipsis;
|
|
1364
|
+
white-space: normal;
|
|
1365
|
+
-webkit-line-clamp: 2;
|
|
1366
|
+
line-clamp: 2;
|
|
1367
|
+
-webkit-box-orient: vertical;
|
|
1368
|
+
flex-shrink: 0;
|
|
1369
|
+
width: 100%;
|
|
1370
|
+
visibility: visible;
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
.buttons {
|
|
1374
|
+
display: flex;
|
|
1375
|
+
flex-direction: column;
|
|
1376
|
+
gap: $CAP_SPACE_04;
|
|
1377
|
+
flex-shrink: 0;
|
|
1378
|
+
width: 100%;
|
|
1379
|
+
visibility: visible;
|
|
1380
|
+
|
|
1381
|
+
.button {
|
|
1382
|
+
display: block;
|
|
1383
|
+
min-height: 1.5rem;
|
|
1384
|
+
border-radius: $CAP_SPACE_12;
|
|
1385
|
+
background: $CAP_PURPLE01;
|
|
1386
|
+
color: $CAP_WHITE;
|
|
1387
|
+
text-align: center;
|
|
1388
|
+
padding: 0.179rem $CAP_SPACE_08;
|
|
1389
|
+
white-space: normal;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
.button-secondary {
|
|
1393
|
+
color: $CAP_PURPLE01;
|
|
1394
|
+
background: transparent;
|
|
1395
|
+
border: none;
|
|
1396
|
+
box-shadow: none;
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1225
1403
|
.whatsapp-doc {
|
|
1226
1404
|
width: 100%;
|
|
1227
1405
|
max-height: 123px;
|
|
@@ -73,6 +73,11 @@ import * as ebillActions from '../Ebill/actions';
|
|
|
73
73
|
import * as emailActions from '../Email/actions';
|
|
74
74
|
import * as lineActions from '../Line/Container/actions';
|
|
75
75
|
import * as viberActions from '../Viber/actions';
|
|
76
|
+
import {
|
|
77
|
+
VIBER_MEDIA_TYPES,
|
|
78
|
+
VIBER_CAROUSEL_TEMPLATES_LIST_CARD_TYPE,
|
|
79
|
+
VIBER_CAROUSEL_TEMPLATES_LIST_STATIC_CLASSNAME,
|
|
80
|
+
} from '../Viber/constants';
|
|
76
81
|
import * as facebookActions from '../Facebook/actions';
|
|
77
82
|
import * as whatsappActions from '../Whatsapp/actions';
|
|
78
83
|
import * as rcsActions from '../Rcs/actions';
|
|
@@ -331,6 +336,17 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
331
336
|
this.handleCloseTestAndPreviewFromListing = this.handleCloseTestAndPreviewFromListing.bind(this);
|
|
332
337
|
}
|
|
333
338
|
|
|
339
|
+
getConnectionProperties(selectedSourceAccountIdentifier, domainPropertiesData = []) {
|
|
340
|
+
if (isEmpty(domainPropertiesData)) return {};
|
|
341
|
+
const matched = domainPropertiesData.find(({ domainProperties }) => {
|
|
342
|
+
const {
|
|
343
|
+
connectionProperties: { account_sid, wabaId, userid, sourceAccountIdentifier, oa_id } = {},
|
|
344
|
+
} = domainProperties || {};
|
|
345
|
+
return [account_sid, wabaId, userid, oa_id, sourceAccountIdentifier].includes(selectedSourceAccountIdentifier);
|
|
346
|
+
});
|
|
347
|
+
return matched?.domainProperties?.connectionProperties || domainPropertiesData[0]?.domainProperties?.connectionProperties || {};
|
|
348
|
+
}
|
|
349
|
+
|
|
334
350
|
// This function is to map the selected source account identifier and the connectionProperties data of domainPropertiesData object to get the hostName.
|
|
335
351
|
getHostName(selectedSourceAccountIdentifier, domainPropertiesData = []) {
|
|
336
352
|
if (!selectedSourceAccountIdentifier || isEmpty(domainPropertiesData)) {
|
|
@@ -987,9 +1003,15 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
987
1003
|
const {name, sourceAccountIdentifier, configs, id } = weCrmAccountsList?.[0] || {};
|
|
988
1004
|
if (isSingleAccount) {
|
|
989
1005
|
weCrmAccountsList[0].hostName = hostName;
|
|
1006
|
+
if (selectedChannel === RCS_LOWERCASE) {
|
|
1007
|
+
weCrmAccountsList[0].connectionProperties = this.getConnectionProperties(weCrmAccountsList[0]?.sourceAccountIdentifier, senderDetails?.domainProperties);
|
|
1008
|
+
}
|
|
990
1009
|
this.setState({ selectedAccount: name });
|
|
991
1010
|
} else {
|
|
992
1011
|
selectedAccount.hostName = hostName;
|
|
1012
|
+
if (selectedChannel === RCS_LOWERCASE) {
|
|
1013
|
+
selectedAccount.connectionProperties = this.getConnectionProperties(selectedAccount?.sourceAccountIdentifier, senderDetails?.domainProperties);
|
|
1014
|
+
}
|
|
993
1015
|
}
|
|
994
1016
|
nextProps.actions.setChannelAccount(selectedChannel?.toUpperCase(), isSingleAccount ? weCrmAccountsList[0] : selectedAccount);
|
|
995
1017
|
if (selectedChannel === ZALO_LOWERCASE) {
|
|
@@ -1099,6 +1121,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
1099
1121
|
if (isZaloOrWhatsappOrRcs) {
|
|
1100
1122
|
hostName = this.getHostName(setAcc?.sourceAccountIdentifier, domainProperties);
|
|
1101
1123
|
setAcc.hostName = hostName;
|
|
1124
|
+
if (selectedChannel === RCS_LOWERCASE) {
|
|
1125
|
+
setAcc.connectionProperties = this.getConnectionProperties(setAcc?.sourceAccountIdentifier, domainProperties);
|
|
1126
|
+
}
|
|
1102
1127
|
}
|
|
1103
1128
|
this.props.actions.setChannelAccount(selectedChannel?.toUpperCase(), setAcc);
|
|
1104
1129
|
|
|
@@ -1405,6 +1430,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
1405
1430
|
const image = viberContent.image || {};
|
|
1406
1431
|
const video = viberContent.video || {};
|
|
1407
1432
|
const button = viberContent.button || {};
|
|
1433
|
+
const messageType = viberContent.type || '';
|
|
1434
|
+
const cardsRaw = viberContent.cards;
|
|
1435
|
+
const cards = Array.isArray(cardsRaw) ? cardsRaw : [];
|
|
1436
|
+
const isCarousel =
|
|
1437
|
+
messageType === VIBER_MEDIA_TYPES.CAROUSEL || cards.length > 0;
|
|
1408
1438
|
|
|
1409
1439
|
const viberPreview = {
|
|
1410
1440
|
messageContent: text,
|
|
@@ -1422,14 +1452,39 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
1422
1452
|
};
|
|
1423
1453
|
}
|
|
1424
1454
|
|
|
1455
|
+
if (isCarousel) {
|
|
1456
|
+
viberPreview.type = VIBER_MEDIA_TYPES.CAROUSEL;
|
|
1457
|
+
viberPreview.cards = cards.map((card) => ({
|
|
1458
|
+
text: card?.text || '',
|
|
1459
|
+
mediaUrl: card?.mediaUrl || '',
|
|
1460
|
+
buttons: (card?.buttons || []).map((btn) => ({
|
|
1461
|
+
title: btn?.title || '',
|
|
1462
|
+
action: btn?.action || '',
|
|
1463
|
+
})),
|
|
1464
|
+
}));
|
|
1465
|
+
viberPreview.showCarouselEditorPreview = true;
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1425
1468
|
// Extract account name
|
|
1426
1469
|
const accountName = get(template, 'definition.accountName', '');
|
|
1427
1470
|
|
|
1428
1471
|
// Return Viber content object (same as Viber/index.js getTemplateContent)
|
|
1429
1472
|
return {
|
|
1430
1473
|
viberPreviewContent: viberPreview,
|
|
1431
|
-
accountName: accountName
|
|
1432
|
-
brandName: accountName
|
|
1474
|
+
accountName: accountName || '',
|
|
1475
|
+
brandName: accountName || '',
|
|
1476
|
+
messageContent: text,
|
|
1477
|
+
...(isCarousel && {
|
|
1478
|
+
type: VIBER_MEDIA_TYPES.CAROUSEL,
|
|
1479
|
+
cards: viberPreview.cards,
|
|
1480
|
+
}),
|
|
1481
|
+
...(!isCarousel && !isEmpty(button) && button.text && (button.url || viberContent.buttonURL) && {
|
|
1482
|
+
button: {
|
|
1483
|
+
text: button.text,
|
|
1484
|
+
url: button.url || viberContent.buttonURL || '',
|
|
1485
|
+
},
|
|
1486
|
+
}),
|
|
1487
|
+
buttonURL: button.url || viberContent.buttonURL || '',
|
|
1433
1488
|
};
|
|
1434
1489
|
}
|
|
1435
1490
|
|
|
@@ -2469,10 +2524,58 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2469
2524
|
image = {},
|
|
2470
2525
|
button = {},
|
|
2471
2526
|
video = {},
|
|
2527
|
+
type = '',
|
|
2528
|
+
cards = [],
|
|
2472
2529
|
} = {},
|
|
2473
2530
|
} = {},
|
|
2474
2531
|
} = template.versions.base;
|
|
2532
|
+
const isViberCarousel = type === VIBER_MEDIA_TYPES.CAROUSEL;
|
|
2475
2533
|
templateData.content = text;
|
|
2534
|
+
if (isViberCarousel) {
|
|
2535
|
+
const previewCards = Array.isArray(cards) ? cards.slice(0, 1) : [];
|
|
2536
|
+
// Bypass CapCustomCard Viber Meta + line-clamp (clips title/buttons for tall images).
|
|
2537
|
+
// cardType hits the default content path; keep VIBER for channel card styles.
|
|
2538
|
+
templateData.cardType = VIBER_CAROUSEL_TEMPLATES_LIST_CARD_TYPE;
|
|
2539
|
+
templateData.className = `${VIBER_CAROUSEL_TEMPLATES_LIST_STATIC_CLASSNAME} ${VIBER}`;
|
|
2540
|
+
templateData.content = (
|
|
2541
|
+
<CapRow vertical className="content">
|
|
2542
|
+
<CapRow className="message-box">
|
|
2543
|
+
<CapLabel type="label1" className="message">
|
|
2544
|
+
{text}
|
|
2545
|
+
</CapLabel>
|
|
2546
|
+
</CapRow>
|
|
2547
|
+
<CapRow className="cards">
|
|
2548
|
+
{previewCards?.map((card, cardIdx) => (
|
|
2549
|
+
<CapRow vertical className="card" key={`viber-carousel-static-card-${cardIdx}`}>
|
|
2550
|
+
{card?.mediaUrl ? (
|
|
2551
|
+
<CapImage src={card.mediaUrl} className="image" />
|
|
2552
|
+
) : (
|
|
2553
|
+
<CapRow className="image-placeholder" />
|
|
2554
|
+
)}
|
|
2555
|
+
<CapLabel type="label1" className="text">
|
|
2556
|
+
{card?.text}
|
|
2557
|
+
</CapLabel>
|
|
2558
|
+
<CapRow vertical className="buttons">
|
|
2559
|
+
{(Array.isArray(card?.buttons) && card.buttons.length
|
|
2560
|
+
? card.buttons
|
|
2561
|
+
: [{}, {}]
|
|
2562
|
+
).slice(0, 2).map((carouselButton, btnIdx) => (
|
|
2563
|
+
<CapLabel
|
|
2564
|
+
key={`viber-carousel-static-btn-${cardIdx}-${btnIdx}`}
|
|
2565
|
+
type="label1"
|
|
2566
|
+
className={`button${btnIdx === 1 ? ' button-secondary' : ''}`}
|
|
2567
|
+
>
|
|
2568
|
+
{(carouselButton?.title || '').trim()}
|
|
2569
|
+
</CapLabel>
|
|
2570
|
+
))}
|
|
2571
|
+
</CapRow>
|
|
2572
|
+
</CapRow>
|
|
2573
|
+
))}
|
|
2574
|
+
</CapRow>
|
|
2575
|
+
</CapRow>
|
|
2576
|
+
);
|
|
2577
|
+
break;
|
|
2578
|
+
}
|
|
2476
2579
|
if (!isEmpty(image)) {
|
|
2477
2580
|
const { url = '' } = image;
|
|
2478
2581
|
templateData.url = url;
|
|
@@ -5143,15 +5246,22 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
5143
5246
|
? this.props.Templates.selectedWhatsappAccount.name
|
|
5144
5247
|
: null
|
|
5145
5248
|
}
|
|
5146
|
-
// Pass formData for
|
|
5249
|
+
// Pass formData for tag extraction (EMAIL/SMS use nested formData; VIBER uses content object)
|
|
5147
5250
|
formData={
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5251
|
+
(() => {
|
|
5252
|
+
const previewContent = this.state.testAndPreviewContent;
|
|
5253
|
+
const channelUpper = this.state.channel?.toUpperCase();
|
|
5254
|
+
if (!previewContent || typeof previewContent !== 'object') {
|
|
5255
|
+
return null;
|
|
5256
|
+
}
|
|
5257
|
+
if ([EMAIL, SMS].includes(channelUpper)) {
|
|
5258
|
+
return previewContent.formData || null;
|
|
5259
|
+
}
|
|
5260
|
+
if (channelUpper === VIBER) {
|
|
5261
|
+
return previewContent;
|
|
5262
|
+
}
|
|
5263
|
+
return null;
|
|
5264
|
+
})()
|
|
5155
5265
|
}
|
|
5156
5266
|
/>
|
|
5157
5267
|
)}
|
|
@@ -47,7 +47,26 @@ export const VIBER_MEDIA_TYPES = {
|
|
|
47
47
|
TEXT: 'TEXT',
|
|
48
48
|
IMAGE: 'IMAGE',
|
|
49
49
|
VIDEO: 'VIDEO',
|
|
50
|
+
CAROUSEL: 'CAROUSEL',
|
|
50
51
|
};
|
|
52
|
+
export const VIBER_CAROUSEL_MIN_CARDS = 2;
|
|
53
|
+
export const VIBER_CAROUSEL_MAX_CARDS = 5;
|
|
54
|
+
export const VIBER_CAROUSEL_MAX_BUTTONS = 2;
|
|
55
|
+
export const VIBER_CAROUSEL_TEMPLATES_LIST_CARD_TYPE = 'VIBER_CAROUSEL_PREVIEW';
|
|
56
|
+
export const VIBER_CAROUSEL_TEMPLATES_LIST_STATIC_CLASSNAME = 'viber-carousel-static';
|
|
57
|
+
export const VIBER_CAROUSEL_CARD_TITLE_MIN_LENGTH = 2;
|
|
58
|
+
export const VIBER_CAROUSEL_CARD_TITLE_MAX_LENGTH = 38;
|
|
59
|
+
export const VIBER_CAROUSEL_FIRST_BUTTON_TITLE_MAX_LENGTH = 10;
|
|
60
|
+
export const VIBER_CAROUSEL_SECOND_BUTTON_TITLE_MAX_LENGTH = 12;
|
|
61
|
+
export const VIBER_CAROUSEL_BUTTON_URL_MAX_LENGTH = 1000;
|
|
62
|
+
export const STATIC_URL = 'STATIC_URL';
|
|
63
|
+
export const DYNAMIC_URL = 'DYNAMIC_URL';
|
|
64
|
+
/** Recommended / validated carousel image height in pixels (passed to image upload). */
|
|
65
|
+
export const VIBER_CAROUSEL_IMG_HEIGHT = 600;
|
|
66
|
+
/** Recommended / validated carousel image width in pixels (passed to image upload). */
|
|
67
|
+
export const VIBER_CAROUSEL_IMG_WIDTH = 696;
|
|
68
|
+
/** Maximum carousel image file size (bytes). 10_000_000 ≈ 10 MB (decimal). */
|
|
69
|
+
export const VIBER_CAROUSEL_IMG_SIZE = 10000000;
|
|
51
70
|
export const ALLOWED_IMAGE_EXTENSIONS_REGEX_VIBER = /\.(jpe?g|png)$/i;
|
|
52
71
|
export const ALLOWED_EXTENSIONS_VIDEO_REGEX_VIBER = /\.(mp4|3gp|m4v|mov)$/i;
|
|
53
72
|
export const NONE = 'NONE';
|
|
@@ -69,6 +88,10 @@ export const mediaRadioOptions = [
|
|
|
69
88
|
value: VIBER_MEDIA_TYPES.VIDEO,
|
|
70
89
|
label: <FormattedMessage {...messages.mediaVideo} />,
|
|
71
90
|
},
|
|
91
|
+
{
|
|
92
|
+
value: VIBER_MEDIA_TYPES.CAROUSEL,
|
|
93
|
+
label: <FormattedMessage {...messages.mediaCarousel} />,
|
|
94
|
+
},
|
|
72
95
|
];
|
|
73
96
|
|
|
74
97
|
export const buttonRadioOptions = [
|