@capillarytech/creatives-library 9.0.44 → 9.0.46
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 +1 -0
- package/package.json +1 -1
- package/v2Components/CommonTestAndPreview/UnifiedPreview/SmsPreviewContent.js +21 -1
- package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberCarouselPreviewCards.js +127 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberPreviewContent.js +106 -15
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +144 -1
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_viberCarouselPreviewCards.scss +133 -0
- package/v2Components/CommonTestAndPreview/constants.js +2 -0
- package/v2Components/CommonTestAndPreview/index.js +241 -26
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/SmsPreviewContent.test.js +32 -0
- 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/v2Components/SmsFallback/constants.js +19 -0
- package/v2Components/SmsFallback/index.js +0 -3
- package/v2Containers/SmsTrai/Edit/constants.js +2 -0
- package/v2Containers/SmsTrai/Edit/dltVarTypes.js +88 -0
- package/v2Containers/SmsTrai/Edit/index.js +206 -96
- package/v2Containers/SmsTrai/Edit/index.scss +117 -1
- package/v2Containers/SmsTrai/Edit/messages.js +40 -0
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +21164 -4559
- package/v2Containers/SmsTrai/Edit/tests/dltVarTypes.test.js +100 -0
- package/v2Containers/Templates/_templates.scss +203 -2
- package/v2Containers/Templates/index.js +193 -42
- package/v2Containers/Templates/messages.js +12 -0
- package/v2Containers/Viber/constants.js +21 -0
- package/v2Containers/Viber/index.js +719 -49
- package/v2Containers/Viber/index.scss +175 -0
- package/v2Containers/Viber/messages.js +121 -0
- package/v2Containers/Viber/tests/index.test.js +80 -0
- package/v2Containers/Whatsapp/index.js +2 -0
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +612 -0
- package/v2Containers/Whatsapp/tests/index.test.js +8 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DLT_VAR_TYPE_KEYS,
|
|
3
|
+
DLT_VAR_TYPE_META,
|
|
4
|
+
DLT_LEGACY_VAR_TOKEN,
|
|
5
|
+
DLT_LEGACY_VAR_REGEX,
|
|
6
|
+
ANY_DLT_HASH_TOKEN_PROBE_REGEX,
|
|
7
|
+
isAnyDltVarToken,
|
|
8
|
+
getDltVarTypeKey,
|
|
9
|
+
getDltVarTypeMeta,
|
|
10
|
+
getDltVarCharLimit,
|
|
11
|
+
} from '../dltVarTypes';
|
|
12
|
+
|
|
13
|
+
describe('dltVarTypes', () => {
|
|
14
|
+
it('exposes the six DLT types with short + long syntax + char limit', () => {
|
|
15
|
+
expect(DLT_VAR_TYPE_KEYS).toEqual([
|
|
16
|
+
'numeric', 'alphanumeric', 'url', 'urlott', 'cbn', 'email',
|
|
17
|
+
]);
|
|
18
|
+
DLT_VAR_TYPE_KEYS.forEach((key) => {
|
|
19
|
+
const meta = DLT_VAR_TYPE_META[key];
|
|
20
|
+
expect(meta.short).toMatch(/^\{#[^#]+#\}$/);
|
|
21
|
+
expect(meta.long).toMatch(/^\{#[^#]+#\}$/);
|
|
22
|
+
expect(typeof meta.charLimit).toBe('number');
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('isAnyDltVarToken', () => {
|
|
27
|
+
it('accepts short and long DLT token forms', () => {
|
|
28
|
+
expect(isAnyDltVarToken('{#num#}')).toBe(true);
|
|
29
|
+
expect(isAnyDltVarToken('{#numeric#}')).toBe(true);
|
|
30
|
+
expect(isAnyDltVarToken('{#var#}')).toBe(true);
|
|
31
|
+
expect(isAnyDltVarToken('{# alp #}')).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('rejects non-DLT strings and non-strings', () => {
|
|
35
|
+
expect(isAnyDltVarToken('{{mustache}}')).toBe(false);
|
|
36
|
+
expect(isAnyDltVarToken('plain text')).toBe(false);
|
|
37
|
+
expect(isAnyDltVarToken('')).toBe(false);
|
|
38
|
+
expect(isAnyDltVarToken(null)).toBe(false);
|
|
39
|
+
expect(isAnyDltVarToken(undefined)).toBe(false);
|
|
40
|
+
expect(isAnyDltVarToken(42)).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('getDltVarTypeKey', () => {
|
|
45
|
+
it('resolves short and long spellings + legacy {#var#} to a type key', () => {
|
|
46
|
+
expect(getDltVarTypeKey('{#num#}')).toBe('numeric');
|
|
47
|
+
expect(getDltVarTypeKey('{#numeric#}')).toBe('numeric');
|
|
48
|
+
expect(getDltVarTypeKey('{#ALP#}')).toBe('alphanumeric');
|
|
49
|
+
expect(getDltVarTypeKey('{# url #}')).toBe('url');
|
|
50
|
+
expect(getDltVarTypeKey(DLT_LEGACY_VAR_TOKEN)).toBe('alphanumeric');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('returns null for unknown / non-string tokens', () => {
|
|
54
|
+
expect(getDltVarTypeKey('{#foobar#}')).toBe(null);
|
|
55
|
+
expect(getDltVarTypeKey('')).toBe(null);
|
|
56
|
+
expect(getDltVarTypeKey(null)).toBe(null);
|
|
57
|
+
expect(getDltVarTypeKey(undefined)).toBe(null);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('getDltVarTypeMeta', () => {
|
|
62
|
+
it('returns the full meta object for a valid token', () => {
|
|
63
|
+
const meta = getDltVarTypeMeta('{#url#}');
|
|
64
|
+
expect(meta.charLimit).toBe(120);
|
|
65
|
+
expect(meta.placeholderKey).toBe('dltVarPlaceholderUrl');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('returns null for an unknown token', () => {
|
|
69
|
+
expect(getDltVarTypeMeta('{#nope#}')).toBe(null);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('getDltVarCharLimit', () => {
|
|
74
|
+
it('returns the type-specific char limit for known tokens', () => {
|
|
75
|
+
expect(getDltVarCharLimit('{#num#}')).toBe(40);
|
|
76
|
+
expect(getDltVarCharLimit('{#url#}')).toBe(120);
|
|
77
|
+
expect(getDltVarCharLimit('{#cbn#}')).toBe(14);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('falls back to the supplied default when the token is unknown', () => {
|
|
81
|
+
expect(getDltVarCharLimit('{#nope#}', 40)).toBe(40);
|
|
82
|
+
expect(getDltVarCharLimit('{#nope#}', 99)).toBe(99);
|
|
83
|
+
expect(getDltVarCharLimit(null)).toBe(40);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('exported regexes', () => {
|
|
88
|
+
it('DLT_LEGACY_VAR_REGEX matches only the legacy generic token', () => {
|
|
89
|
+
expect(DLT_LEGACY_VAR_REGEX.test('body {#var#} tail')).toBe(true);
|
|
90
|
+
expect(DLT_LEGACY_VAR_REGEX.test('body {#VAR#} tail')).toBe(true);
|
|
91
|
+
expect(DLT_LEGACY_VAR_REGEX.test('body {#num#} tail')).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('ANY_DLT_HASH_TOKEN_PROBE_REGEX detects any hash token safely across calls', () => {
|
|
95
|
+
expect(ANY_DLT_HASH_TOKEN_PROBE_REGEX.test('hi {#alp#} there')).toBe(true);
|
|
96
|
+
expect(ANY_DLT_HASH_TOKEN_PROBE_REGEX.test('hi {#url#} again')).toBe(true);
|
|
97
|
+
expect(ANY_DLT_HASH_TOKEN_PROBE_REGEX.test('plain')).toBe(false);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -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;
|
|
@@ -1480,12 +1658,35 @@
|
|
|
1480
1658
|
cursor: not-allowed;
|
|
1481
1659
|
}
|
|
1482
1660
|
|
|
1661
|
+
.sms-service-explicit-retired-banner.ant-alert {
|
|
1662
|
+
margin: 0 $CAP_SPACE_16 $CAP_SPACE_16 $CAP_SPACE_16;
|
|
1663
|
+
padding: $CAP_SPACE_08 $CAP_SPACE_12;
|
|
1664
|
+
align-items: center;
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
.sms-template-category-tag {
|
|
1668
|
+
margin-bottom: $CAP_SPACE_12;
|
|
1669
|
+
padding: 0 $CAP_SPACE_12;
|
|
1670
|
+
border-radius: 62.5rem;
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
|
|
1674
|
+
.sms-template-legacy-warning.ant-alert {
|
|
1675
|
+
margin-top: $CAP_SPACE_12;
|
|
1676
|
+
|
|
1677
|
+
.ant-alert-title,
|
|
1678
|
+
.ant-alert-title span {
|
|
1679
|
+
font-weight: 400;
|
|
1680
|
+
white-space: normal;
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1483
1684
|
.template-action-dropdown {
|
|
1484
1685
|
min-width: 14.286rem !important;
|
|
1485
1686
|
.template-action-menu {
|
|
1486
1687
|
padding: 4px 0px;
|
|
1487
1688
|
.template-action-menu-item {
|
|
1488
|
-
padding:
|
|
1689
|
+
padding: $CAP_SPACE_12 $CAP_SPACE_24;
|
|
1489
1690
|
}
|
|
1490
1691
|
}
|
|
1491
1692
|
}
|
|
@@ -73,6 +73,7 @@ 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 { VIBER_MEDIA_TYPES } from '../Viber/constants';
|
|
76
77
|
import * as facebookActions from '../Facebook/actions';
|
|
77
78
|
import * as whatsappActions from '../Whatsapp/actions';
|
|
78
79
|
import * as rcsActions from '../Rcs/actions';
|
|
@@ -142,7 +143,10 @@ import {CREATIVE} from '../Facebook/constants';
|
|
|
142
143
|
import videoPlay from '../../assets/videoPlay.svg';
|
|
143
144
|
import whatsappImageEmptyPreview from '../../v2Components/TemplatePreview/assets/images/empty_image_preview.svg';
|
|
144
145
|
import whatsappVideoEmptyPreview from '../../v2Components/TemplatePreview/assets/images/empty_video_preview.svg';
|
|
145
|
-
import {
|
|
146
|
+
import {
|
|
147
|
+
CAP_SPACE_16, CAP_G08, CAP_G05, CAP_SPACE_08, CAP_SPACE_12, CAP_YELLOW01,
|
|
148
|
+
} from '@capillarytech/cap-ui-library/styled/variables';
|
|
149
|
+
import { SMS_CATEGORY_TAG_STYLES, SMS_CATEGORY_LABELS } from '../../v2Components/SmsFallback/constants';
|
|
146
150
|
import { GA } from '@capillarytech/cap-ui-utils';
|
|
147
151
|
import { MAPP_SDK } from '../InApp/constants';
|
|
148
152
|
import injectReducer from '../../utils/injectReducer';
|
|
@@ -158,6 +162,7 @@ import { v2MobilePushSagas } from '../MobilePushNew/sagas';
|
|
|
158
162
|
import { AUTO_CAROUSEL, BIG_PICTURE, FILMSTRIP_CAROUSEL, MANUAL_CAROUSEL } from '../MobilePushNew/constants';
|
|
159
163
|
import CapPageSpinner from '../../v2Components/CapPageSpinner';
|
|
160
164
|
import webPushSagas from '../WebPush/sagas';
|
|
165
|
+
import { DLT_LEGACY_VAR_REGEX } from '../SmsTrai/Edit/dltVarTypes';
|
|
161
166
|
const withMobilePushNewSaga = injectSaga({ key: 'mobilePushNew', saga: v2MobilePushSagas, mode: DAEMON });
|
|
162
167
|
const withWebPushSaga = injectSaga({ key: 'webPush', saga: webPushSagas, mode: DAEMON });
|
|
163
168
|
|
|
@@ -454,9 +459,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
454
459
|
}
|
|
455
460
|
|
|
456
461
|
checkDLTfeatureEnable() {
|
|
457
|
-
const {smsRegister, isFullMode} = this.props;
|
|
458
|
-
|
|
459
|
-
|
|
462
|
+
const { smsRegister, isFullMode, localTemplatesConfig } = this.props;
|
|
463
|
+
if (commonUtil.isTraiDLTEnable(isFullMode, smsRegister)) return true;
|
|
464
|
+
// Local-templates picker (RCS SMS fallback, campaigns): apply DLT UI whenever the org has
|
|
465
|
+
// the feature, even if the parent context wasn't marked `smsRegister === 'DLT'`.
|
|
466
|
+
return !!localTemplatesConfig?.useLocalTemplates && commonUtil.hasTraiDltFeature();
|
|
460
467
|
}
|
|
461
468
|
|
|
462
469
|
componentDidMount() {
|
|
@@ -1399,6 +1406,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
1399
1406
|
const image = viberContent.image || {};
|
|
1400
1407
|
const video = viberContent.video || {};
|
|
1401
1408
|
const button = viberContent.button || {};
|
|
1409
|
+
const messageType = viberContent.type || '';
|
|
1410
|
+
const cardsRaw = viberContent.cards;
|
|
1411
|
+
const cards = Array.isArray(cardsRaw) ? cardsRaw : [];
|
|
1412
|
+
const isCarousel =
|
|
1413
|
+
messageType === VIBER_MEDIA_TYPES.CAROUSEL || cards.length > 0;
|
|
1402
1414
|
|
|
1403
1415
|
const viberPreview = {
|
|
1404
1416
|
messageContent: text,
|
|
@@ -1416,14 +1428,39 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
1416
1428
|
};
|
|
1417
1429
|
}
|
|
1418
1430
|
|
|
1431
|
+
if (isCarousel) {
|
|
1432
|
+
viberPreview.type = VIBER_MEDIA_TYPES.CAROUSEL;
|
|
1433
|
+
viberPreview.cards = cards.map((card) => ({
|
|
1434
|
+
text: card?.text || '',
|
|
1435
|
+
mediaUrl: card?.mediaUrl || '',
|
|
1436
|
+
buttons: (card?.buttons || []).map((btn) => ({
|
|
1437
|
+
title: btn?.title || '',
|
|
1438
|
+
action: btn?.action || '',
|
|
1439
|
+
})),
|
|
1440
|
+
}));
|
|
1441
|
+
viberPreview.showCarouselEditorPreview = true;
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1419
1444
|
// Extract account name
|
|
1420
1445
|
const accountName = get(template, 'definition.accountName', '');
|
|
1421
1446
|
|
|
1422
1447
|
// Return Viber content object (same as Viber/index.js getTemplateContent)
|
|
1423
1448
|
return {
|
|
1424
1449
|
viberPreviewContent: viberPreview,
|
|
1425
|
-
accountName: accountName
|
|
1426
|
-
brandName: accountName
|
|
1450
|
+
accountName: accountName || '',
|
|
1451
|
+
brandName: accountName || '',
|
|
1452
|
+
messageContent: text,
|
|
1453
|
+
...(isCarousel && {
|
|
1454
|
+
type: VIBER_MEDIA_TYPES.CAROUSEL,
|
|
1455
|
+
cards: viberPreview.cards,
|
|
1456
|
+
}),
|
|
1457
|
+
...(!isCarousel && !isEmpty(button) && button.text && (button.url || viberContent.buttonURL) && {
|
|
1458
|
+
button: {
|
|
1459
|
+
text: button.text,
|
|
1460
|
+
url: button.url || viberContent.buttonURL || '',
|
|
1461
|
+
},
|
|
1462
|
+
}),
|
|
1463
|
+
buttonURL: button.url || viberContent.buttonURL || '',
|
|
1427
1464
|
};
|
|
1428
1465
|
}
|
|
1429
1466
|
|
|
@@ -2045,11 +2082,20 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2045
2082
|
const isCardArchiveEligible = isArchivalEnabled && this.isChannelArchiveEligible(currentChannel, cardWhatsappStatus, cardRcsStatus);
|
|
2046
2083
|
const isArchivedMode = isArchivalEnabled && get(this.props, 'Templates.isArchivedMode', false);
|
|
2047
2084
|
const isAnyArchiveInProgress = isArchivalEnabled && !!(get(this.props, 'Templates.archiveInProgress') || get(this.props, 'Templates.unarchiveInProgress') || get(this.props, 'Templates.bulkArchiveInProgress') || get(this.props, 'Templates.bulkUnarchiveInProgress'));
|
|
2085
|
+
const smsBaseForLegacyCheck = template?.versions?.base || {};
|
|
2086
|
+
const updatedSmsEditorForLegacy = smsBaseForLegacyCheck['updated-sms-editor'];
|
|
2087
|
+
const smsBodyForLegacyCheck = Array.isArray(updatedSmsEditorForLegacy)
|
|
2088
|
+
? updatedSmsEditorForLegacy.join('')
|
|
2089
|
+
: updatedSmsEditorForLegacy ?? smsBaseForLegacyCheck['sms-editor'] ?? '';
|
|
2090
|
+
const isDltLegacyTemplate = currentChannel === SMS
|
|
2091
|
+
&& isTraiDltFeature
|
|
2092
|
+
&& !this.props.isFullMode
|
|
2093
|
+
&& DLT_LEGACY_VAR_REGEX.test(smsBodyForLegacyCheck);
|
|
2048
2094
|
const templateData = {
|
|
2049
2095
|
key: `${currentChannel}-card-${template?.name}`,
|
|
2050
2096
|
title: (
|
|
2051
2097
|
<span className="template-card-title" title={template?.name}>
|
|
2052
|
-
{isCardArchiveEligible && this.renderCardSelectionCheckbox({ templateId: template._id, selectedIds: selectedIdsArrayForCard, isDisabled: isAnyArchiveInProgress })}
|
|
2098
|
+
{isCardArchiveEligible && this.renderCardSelectionCheckbox({ templateId: template._id, selectedIds: selectedIdsArrayForCard, isDisabled: isAnyArchiveInProgress || isDltLegacyTemplate })}
|
|
2053
2099
|
<CapLabel.CapLabelInline type="label1" title={template?.name} className="template-card-name">
|
|
2054
2100
|
{template?.name}
|
|
2055
2101
|
{currentChannel === INAPP && (
|
|
@@ -2109,20 +2155,39 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2109
2155
|
})()
|
|
2110
2156
|
],
|
|
2111
2157
|
hoverOption: isArchivedMode || !this.canPerform(PERMISSIONS.CREATIVE_EDIT) ? null : (
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
this.props.
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2158
|
+
isDltLegacyTemplate ? (
|
|
2159
|
+
<CapTooltip
|
|
2160
|
+
title={this.props.intl.formatMessage(messages.smsLegacyBlockedTooltip)}
|
|
2161
|
+
>
|
|
2162
|
+
<CapLabel.CapLabelInline>
|
|
2163
|
+
<CapButton
|
|
2164
|
+
className={
|
|
2165
|
+
this.props.isFullMode
|
|
2166
|
+
? `edit-${channelLowerCase}`
|
|
2167
|
+
: `select-${channelLowerCase}`
|
|
2168
|
+
}
|
|
2169
|
+
disabled
|
|
2170
|
+
>
|
|
2171
|
+
{hoverButtonText}
|
|
2172
|
+
</CapButton>
|
|
2173
|
+
</CapLabel.CapLabelInline>
|
|
2174
|
+
</CapTooltip>
|
|
2175
|
+
) : (
|
|
2176
|
+
<CapButton
|
|
2177
|
+
className={
|
|
2178
|
+
this.props.isFullMode
|
|
2179
|
+
? `edit-${channelLowerCase}`
|
|
2180
|
+
: `select-${channelLowerCase}`
|
|
2181
|
+
}
|
|
2182
|
+
onClick={e =>
|
|
2183
|
+
handlers.handleEditClick(e, template, undefined, undefined, {
|
|
2184
|
+
account: this.state.selectedAccount
|
|
2185
|
+
})
|
|
2186
|
+
}
|
|
2187
|
+
>
|
|
2188
|
+
{hoverButtonText}
|
|
2189
|
+
</CapButton>
|
|
2190
|
+
)
|
|
2126
2191
|
)
|
|
2127
2192
|
};
|
|
2128
2193
|
const {
|
|
@@ -2222,22 +2287,49 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2222
2287
|
);
|
|
2223
2288
|
}
|
|
2224
2289
|
switch (currentChannel) {
|
|
2225
|
-
case SMS:
|
|
2290
|
+
case SMS: {
|
|
2291
|
+
const smsBaseForCard = template.versions.base || {};
|
|
2292
|
+
const updatedSmsEditor = smsBaseForCard['updated-sms-editor'];
|
|
2293
|
+
const normalizedUpdatedSmsEditor = Array.isArray(updatedSmsEditor)
|
|
2294
|
+
? updatedSmsEditor.join('')
|
|
2295
|
+
: updatedSmsEditor;
|
|
2296
|
+
const smsBodyForCard = normalizedUpdatedSmsEditor || smsBaseForCard['sms-editor'] || '';
|
|
2297
|
+
const hasLegacyVarToken = DLT_LEGACY_VAR_REGEX.test(smsBodyForCard);
|
|
2298
|
+
const rawCategoryLabel = smsBaseForCard.type || '';
|
|
2299
|
+
const categoryLabel = rawCategoryLabel.toLowerCase() === SMS_CATEGORY_LABELS.SERVICE_EXPLICIT
|
|
2300
|
+
? this.props.intl.formatMessage(messages.promotional)
|
|
2301
|
+
: rawCategoryLabel;
|
|
2302
|
+
const categoryTagStyle = SMS_CATEGORY_TAG_STYLES[categoryLabel.toLowerCase()] || {};
|
|
2226
2303
|
templateData.content = isTraiDltFeature ? (
|
|
2227
2304
|
<>
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2305
|
+
{hasLegacyVarToken ? (
|
|
2306
|
+
<CapColoredTag
|
|
2307
|
+
tagColor="rgba(254, 197, 46, 0.15)"
|
|
2308
|
+
tagTextColor={CAP_YELLOW01}
|
|
2309
|
+
tagHeight="1.25rem"
|
|
2310
|
+
tagFontSize="0.75rem"
|
|
2311
|
+
className="sms-template-category-tag"
|
|
2312
|
+
>
|
|
2313
|
+
{this.props.intl.formatMessage(messages.smsLegacyFormatBadge)}
|
|
2314
|
+
</CapColoredTag>
|
|
2315
|
+
) : categoryLabel ? (
|
|
2316
|
+
<CapColoredTag
|
|
2317
|
+
tagColor={categoryTagStyle.tagColor}
|
|
2318
|
+
tagTextColor={categoryTagStyle.tagTextColor}
|
|
2319
|
+
tagHeight="1.25rem"
|
|
2320
|
+
tagFontSize="0.75rem"
|
|
2321
|
+
className="sms-template-category-tag"
|
|
2322
|
+
>
|
|
2323
|
+
{categoryLabel}
|
|
2324
|
+
</CapColoredTag>
|
|
2325
|
+
) : null}
|
|
2326
|
+
<CapLabel type="label1">{smsBodyForCard}</CapLabel>
|
|
2236
2327
|
</>
|
|
2237
2328
|
) : (
|
|
2238
2329
|
template.versions.base['sms-editor']
|
|
2239
2330
|
);
|
|
2240
2331
|
break;
|
|
2332
|
+
}
|
|
2241
2333
|
case EMAIL: {
|
|
2242
2334
|
const url = template.versions.base.preview_http_url;
|
|
2243
2335
|
if (url) {
|
|
@@ -2436,10 +2528,59 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2436
2528
|
image = {},
|
|
2437
2529
|
button = {},
|
|
2438
2530
|
video = {},
|
|
2531
|
+
type = '',
|
|
2532
|
+
cards = [],
|
|
2439
2533
|
} = {},
|
|
2440
2534
|
} = {},
|
|
2441
2535
|
} = template.versions.base;
|
|
2536
|
+
const isViberCarousel = type === VIBER_MEDIA_TYPES.CAROUSEL;
|
|
2442
2537
|
templateData.content = text;
|
|
2538
|
+
if (isViberCarousel) {
|
|
2539
|
+
const previewCards = Array.isArray(cards) ? cards.slice(0, 1) : [];
|
|
2540
|
+
// Bypass CapCustomCard Viber Meta + line-clamp (clips title/buttons for tall images).
|
|
2541
|
+
// Parent class `viber-carousel-static` scopes listing styles in _templates.scss.
|
|
2542
|
+
// cardType hits the default content path; keep VIBER for channel card styles.
|
|
2543
|
+
templateData.cardType = 'VIBER_CAROUSEL_PREVIEW';
|
|
2544
|
+
templateData.className = 'viber-carousel-static VIBER';
|
|
2545
|
+
templateData.content = (
|
|
2546
|
+
<CapRow vertical className="content">
|
|
2547
|
+
<CapRow className="message-box">
|
|
2548
|
+
<CapLabel type="label1" className="message">
|
|
2549
|
+
{text}
|
|
2550
|
+
</CapLabel>
|
|
2551
|
+
</CapRow>
|
|
2552
|
+
<CapRow className="cards">
|
|
2553
|
+
{previewCards?.map((card, cardIdx) => (
|
|
2554
|
+
<CapRow vertical className="card" key={`viber-carousel-static-card-${cardIdx}`}>
|
|
2555
|
+
{card?.mediaUrl ? (
|
|
2556
|
+
<CapImage src={card.mediaUrl} className="image" />
|
|
2557
|
+
) : (
|
|
2558
|
+
<CapRow className="image-placeholder" />
|
|
2559
|
+
)}
|
|
2560
|
+
<CapLabel type="label1" className="text">
|
|
2561
|
+
{card?.text}
|
|
2562
|
+
</CapLabel>
|
|
2563
|
+
<CapRow vertical className="buttons">
|
|
2564
|
+
{(Array.isArray(card?.buttons) && card.buttons.length
|
|
2565
|
+
? card.buttons
|
|
2566
|
+
: [{}, {}]
|
|
2567
|
+
).slice(0, 2).map((carouselButton, btnIdx) => (
|
|
2568
|
+
<CapLabel
|
|
2569
|
+
key={`viber-carousel-static-btn-${cardIdx}-${btnIdx}`}
|
|
2570
|
+
type="label1"
|
|
2571
|
+
className={`button${btnIdx === 1 ? ' button-secondary' : ''}`}
|
|
2572
|
+
>
|
|
2573
|
+
{(carouselButton?.title || '').trim()}
|
|
2574
|
+
</CapLabel>
|
|
2575
|
+
))}
|
|
2576
|
+
</CapRow>
|
|
2577
|
+
</CapRow>
|
|
2578
|
+
))}
|
|
2579
|
+
</CapRow>
|
|
2580
|
+
</CapRow>
|
|
2581
|
+
);
|
|
2582
|
+
break;
|
|
2583
|
+
}
|
|
2443
2584
|
if (!isEmpty(image)) {
|
|
2444
2585
|
const { url = '' } = image;
|
|
2445
2586
|
templateData.url = url;
|
|
@@ -2761,6 +2902,12 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2761
2902
|
)}
|
|
2762
2903
|
</CapRow>
|
|
2763
2904
|
{[WHATSAPP, ZALO, INAPP,RCS].includes(currentChannel) && this.selectedFilters()}
|
|
2905
|
+
{currentChannel === SMS && this.checkDLTfeatureEnable() && (
|
|
2906
|
+
<CapInfoNote
|
|
2907
|
+
className="sms-service-explicit-retired-banner"
|
|
2908
|
+
message={this.props.intl.formatMessage(messages.smsServiceExplicitRetiredBanner)}
|
|
2909
|
+
/>
|
|
2910
|
+
)}
|
|
2764
2911
|
{<div>
|
|
2765
2912
|
{!isEmpty(filteredTemplates) || !isEmpty(this.state.searchText) || !isEmpty(this.props.Templates.templateError) ? (
|
|
2766
2913
|
<div className={!isEmpty(this.state.searchText) && isEmpty(cardDataList) ? '' : this.isFullMode() ? "v2-pagination-container" : "v2-pagination-container-half"}>
|
|
@@ -4788,7 +4935,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4788
4935
|
<CapRadio.CapRadioGroup className="line-filters" defaultValue={smsFilter} onChange={this.setSMSFilter}>
|
|
4789
4936
|
{
|
|
4790
4937
|
(() => {
|
|
4791
|
-
const { ALL, SERVICE_IMPLICIT,
|
|
4938
|
+
const { ALL, SERVICE_IMPLICIT, PROMOTIONAL } = SMS_FILTERS;
|
|
4792
4939
|
return (
|
|
4793
4940
|
<>
|
|
4794
4941
|
<CapRadio.Button value={ALL}><CapLabel type="label2">
|
|
@@ -4797,9 +4944,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4797
4944
|
<CapRadio.Button value={PROMOTIONAL}><CapLabel type="label2">
|
|
4798
4945
|
<FormattedMessage {...messages.promotional} />
|
|
4799
4946
|
</CapLabel></CapRadio.Button>
|
|
4800
|
-
<CapRadio.Button value={SERVICE_EXPLICIT}><CapLabel type="label2">
|
|
4801
|
-
<FormattedMessage {...messages.serviceExplicit} />
|
|
4802
|
-
</CapLabel></CapRadio.Button>
|
|
4803
4947
|
<CapRadio.Button value={SERVICE_IMPLICIT}><CapLabel type="label2">
|
|
4804
4948
|
<FormattedMessage {...messages.serviceImplicit} />
|
|
4805
4949
|
</CapLabel></CapRadio.Button>
|
|
@@ -5107,15 +5251,22 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
5107
5251
|
? this.props.Templates.selectedWhatsappAccount.name
|
|
5108
5252
|
: null
|
|
5109
5253
|
}
|
|
5110
|
-
// Pass formData for
|
|
5254
|
+
// Pass formData for tag extraction (EMAIL/SMS use nested formData; VIBER uses content object)
|
|
5111
5255
|
formData={
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5256
|
+
(() => {
|
|
5257
|
+
const previewContent = this.state.testAndPreviewContent;
|
|
5258
|
+
const channelUpper = this.state.channel?.toUpperCase();
|
|
5259
|
+
if (!previewContent || typeof previewContent !== 'object') {
|
|
5260
|
+
return null;
|
|
5261
|
+
}
|
|
5262
|
+
if ([EMAIL, SMS].includes(channelUpper)) {
|
|
5263
|
+
return previewContent.formData || null;
|
|
5264
|
+
}
|
|
5265
|
+
if (channelUpper === VIBER) {
|
|
5266
|
+
return previewContent;
|
|
5267
|
+
}
|
|
5268
|
+
return null;
|
|
5269
|
+
})()
|
|
5119
5270
|
}
|
|
5120
5271
|
/>
|
|
5121
5272
|
)}
|