@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,133 @@
|
|
|
1
|
+
// Shared Viber carousel card row (Test & Preview device preview)
|
|
2
|
+
.viber-carousel-preview {
|
|
3
|
+
.scroll {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: row;
|
|
6
|
+
flex-wrap: nowrap;
|
|
7
|
+
align-items: stretch;
|
|
8
|
+
width: 100%;
|
|
9
|
+
max-width: 100%;
|
|
10
|
+
min-width: 0;
|
|
11
|
+
overflow-x: auto;
|
|
12
|
+
overflow-y: hidden;
|
|
13
|
+
padding-right: $CAP_SPACE_12;
|
|
14
|
+
scrollbar-width: none;
|
|
15
|
+
-webkit-overflow-scrolling: touch;
|
|
16
|
+
|
|
17
|
+
&::-webkit-scrollbar {
|
|
18
|
+
display: none;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.card {
|
|
22
|
+
flex: 0 0 68%;
|
|
23
|
+
min-width: 80%;
|
|
24
|
+
margin-right: $CAP_SPACE_08;
|
|
25
|
+
background: $CAP_G09;
|
|
26
|
+
border: 1px solid $CAP_G07;
|
|
27
|
+
border-radius: $CAP_SPACE_12;
|
|
28
|
+
overflow: hidden;
|
|
29
|
+
display: flex;
|
|
30
|
+
flex-direction: column;
|
|
31
|
+
align-self: stretch;
|
|
32
|
+
|
|
33
|
+
&:last-child {
|
|
34
|
+
margin-right: 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.image {
|
|
38
|
+
width: 100%;
|
|
39
|
+
height: 10rem;
|
|
40
|
+
object-fit: cover;
|
|
41
|
+
border-radius: 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.image-placeholder {
|
|
45
|
+
width: 100%;
|
|
46
|
+
height: 10rem;
|
|
47
|
+
border-radius: 0;
|
|
48
|
+
background: $CAP_G07;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.card-body {
|
|
52
|
+
flex: 1;
|
|
53
|
+
padding: $CAP_SPACE_08;
|
|
54
|
+
display: flex;
|
|
55
|
+
flex-direction: column;
|
|
56
|
+
min-height: 0;
|
|
57
|
+
|
|
58
|
+
.text-wrap {
|
|
59
|
+
flex-shrink: 0;
|
|
60
|
+
width: 100%;
|
|
61
|
+
|
|
62
|
+
&--1-line {
|
|
63
|
+
min-height: 1.3em;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
&--2-line {
|
|
67
|
+
min-height: 2.6em;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.text-inner {
|
|
71
|
+
width: 100%;
|
|
72
|
+
|
|
73
|
+
.text {
|
|
74
|
+
color: $CAP_G01;
|
|
75
|
+
line-height: 1.3;
|
|
76
|
+
white-space: normal;
|
|
77
|
+
word-break: break-word;
|
|
78
|
+
display: block;
|
|
79
|
+
width: 100%;
|
|
80
|
+
margin: 0;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.text-placeholder {
|
|
85
|
+
width: 100%;
|
|
86
|
+
height: 0.875rem;
|
|
87
|
+
border-radius: $CAP_SPACE_04;
|
|
88
|
+
background: $CAP_G07;
|
|
89
|
+
min-height: 0.875rem;
|
|
90
|
+
display: block;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.buttons {
|
|
95
|
+
display: flex;
|
|
96
|
+
flex-direction: column;
|
|
97
|
+
gap: $CAP_SPACE_06;
|
|
98
|
+
width: 100%;
|
|
99
|
+
flex-shrink: 0;
|
|
100
|
+
|
|
101
|
+
.button {
|
|
102
|
+
color: $CAP_WHITE;
|
|
103
|
+
background: $CAP_PURPLE01;
|
|
104
|
+
border-radius: $CAP_SPACE_12;
|
|
105
|
+
text-align: center;
|
|
106
|
+
width: 100%;
|
|
107
|
+
display: flex;
|
|
108
|
+
align-items: center;
|
|
109
|
+
justify-content: center;
|
|
110
|
+
min-height: 1.5rem;
|
|
111
|
+
padding: $CAP_SPACE_06 $CAP_SPACE_08;
|
|
112
|
+
white-space: normal;
|
|
113
|
+
box-sizing: border-box;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.button-placeholder {
|
|
117
|
+
visibility: hidden;
|
|
118
|
+
pointer-events: none;
|
|
119
|
+
padding: 0;
|
|
120
|
+
background: transparent;
|
|
121
|
+
border: none;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.button-secondary {
|
|
125
|
+
color: $CAP_PURPLE01;
|
|
126
|
+
background: transparent;
|
|
127
|
+
border: none;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -228,6 +228,8 @@ export const CHANNEL_NAME_MOBILE_PUSH = 'MOBILE_PUSH';
|
|
|
228
228
|
export const DEFAULT_SENDER = 'test1';
|
|
229
229
|
export const VIBER_API_SCENARIO_KEY = 'viber-api';
|
|
230
230
|
export const VIBER_USER_ID_PATTERN = '{{viber_user_id}}';
|
|
231
|
+
/** Fallback title line-height (px) when computed style is unavailable. */
|
|
232
|
+
export const DEFAULT_CAROUSEL_TITLE_LINE_HEIGHT_PX = 18;
|
|
231
233
|
export const UNSUBSCRIBE_TAG_NAME = 'unsubscribe';
|
|
232
234
|
export const IN_APP_CHANNEL_NAME = 'in-app';
|
|
233
235
|
export const MOBILE_PUSH_CHANNEL_NAME = 'mobile push';
|
|
@@ -246,6 +246,8 @@ const CommonTestAndPreview = (props) => {
|
|
|
246
246
|
const [smsFallbackPreviewText, setSmsFallbackPreviewText] = useState(undefined);
|
|
247
247
|
// Track if a preview call has been made (to know when to use previewDataHtml vs raw content)
|
|
248
248
|
const [hasPreviewCallBeenMade, setHasPreviewCallBeenMade] = useState(false);
|
|
249
|
+
// Viber: tag values applied to preview only after explicit Update preview / Discard (not while typing)
|
|
250
|
+
const [viberPreviewTagValues, setViberPreviewTagValues] = useState(null);
|
|
249
251
|
const [previewDataHtml, setPreviewDataHtml] = useState(() => {
|
|
250
252
|
// Initialize preview data based on channel
|
|
251
253
|
if (channel === CHANNELS.EMAIL && formData) {
|
|
@@ -687,6 +689,159 @@ const CommonTestAndPreview = (props) => {
|
|
|
687
689
|
return resolvedText;
|
|
688
690
|
};
|
|
689
691
|
|
|
692
|
+
const getViberMergedFormData = useCallback((formDataOverride) => {
|
|
693
|
+
const formDataObj = formDataOverride && typeof formDataOverride === 'object'
|
|
694
|
+
? formDataOverride
|
|
695
|
+
: (formData && typeof formData === 'object' ? formData : {});
|
|
696
|
+
let contentObj = {};
|
|
697
|
+
if (content && typeof content === 'object') {
|
|
698
|
+
contentObj = content;
|
|
699
|
+
} else if (typeof content === 'string' && content.trim()) {
|
|
700
|
+
try {
|
|
701
|
+
contentObj = JSON.parse(content);
|
|
702
|
+
} catch (e) {
|
|
703
|
+
contentObj = {};
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
const previewCards = formDataObj?.viberPreviewContent?.cards
|
|
707
|
+
?? formDataObj?.cards
|
|
708
|
+
?? contentObj?.viberPreviewContent?.cards
|
|
709
|
+
?? contentObj?.cards;
|
|
710
|
+
const isCarousel = Boolean(
|
|
711
|
+
formDataObj.type === MEDIA_TYPE_CAROUSEL
|
|
712
|
+
|| contentObj.type === MEDIA_TYPE_CAROUSEL
|
|
713
|
+
|| (Array.isArray(previewCards) && previewCards.length > 0),
|
|
714
|
+
);
|
|
715
|
+
const mergedPreview = {
|
|
716
|
+
...(contentObj.viberPreviewContent || {}),
|
|
717
|
+
...(formDataObj.viberPreviewContent || {}),
|
|
718
|
+
...(Array.isArray(previewCards) && previewCards.length ? { cards: previewCards } : {}),
|
|
719
|
+
...(isCarousel ? {
|
|
720
|
+
type: MEDIA_TYPE_CAROUSEL,
|
|
721
|
+
showCarouselEditorPreview: true,
|
|
722
|
+
} : {}),
|
|
723
|
+
};
|
|
724
|
+
return {
|
|
725
|
+
...contentObj,
|
|
726
|
+
...formDataObj,
|
|
727
|
+
...(Array.isArray(previewCards) && previewCards.length ? { cards: previewCards } : {}),
|
|
728
|
+
...(isCarousel ? { type: MEDIA_TYPE_CAROUSEL } : {}),
|
|
729
|
+
...(Object.keys(mergedPreview).length ? { viberPreviewContent: mergedPreview } : {}),
|
|
730
|
+
};
|
|
731
|
+
}, [formData, content]);
|
|
732
|
+
|
|
733
|
+
const getViberCarouselCardsFromFormData = (formDataObj) => {
|
|
734
|
+
const previewCards = formDataObj?.viberPreviewContent?.cards;
|
|
735
|
+
if (Array.isArray(previewCards) && previewCards.length) {
|
|
736
|
+
return previewCards;
|
|
737
|
+
}
|
|
738
|
+
const rootCards = formDataObj?.cards;
|
|
739
|
+
if (Array.isArray(rootCards) && rootCards.length) {
|
|
740
|
+
return rootCards;
|
|
741
|
+
}
|
|
742
|
+
return [];
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
const getViberTagExtractionContent = (formDataObj, contentStr = '') => {
|
|
746
|
+
const messageText = formDataObj?.messageContent
|
|
747
|
+
|| formDataObj?.viberPreviewContent?.messageContent
|
|
748
|
+
|| contentStr
|
|
749
|
+
|| '';
|
|
750
|
+
const cardFieldTexts = getViberCarouselCardsFromFormData(formDataObj).flatMap((card) => {
|
|
751
|
+
const fields = [card?.text || ''];
|
|
752
|
+
(card?.buttons || []).forEach((button) => {
|
|
753
|
+
fields.push(button?.title || '', button?.action || '');
|
|
754
|
+
});
|
|
755
|
+
return fields;
|
|
756
|
+
});
|
|
757
|
+
return [messageText, ...cardFieldTexts]
|
|
758
|
+
.filter((value) => typeof value === 'string' && value.trim())
|
|
759
|
+
.join(' ');
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
const resolveViberCarouselCards = (cards, tagValues) => {
|
|
763
|
+
if (!Array.isArray(cards) || !tagValues || !Object.keys(tagValues).length) {
|
|
764
|
+
return cards;
|
|
765
|
+
}
|
|
766
|
+
return cards.map((card) => ({
|
|
767
|
+
...card,
|
|
768
|
+
text: resolveTagsInText(card?.text || '', tagValues),
|
|
769
|
+
buttons: (card?.buttons || []).map((button) => ({
|
|
770
|
+
...button,
|
|
771
|
+
title: resolveTagsInText(button?.title || '', tagValues),
|
|
772
|
+
action: resolveTagsInText(button?.action || '', tagValues),
|
|
773
|
+
})),
|
|
774
|
+
}));
|
|
775
|
+
};
|
|
776
|
+
|
|
777
|
+
const applyViberCustomValuesToContent = (contentObj, tagValues) => {
|
|
778
|
+
if (!contentObj || typeof contentObj !== 'object' || !tagValues || !Object.keys(tagValues).length) {
|
|
779
|
+
return contentObj;
|
|
780
|
+
}
|
|
781
|
+
const result = { ...contentObj };
|
|
782
|
+
if (typeof result.messageContent === 'string') {
|
|
783
|
+
result.messageContent = resolveTagsInText(result.messageContent, tagValues);
|
|
784
|
+
}
|
|
785
|
+
if (Array.isArray(result.cards)) {
|
|
786
|
+
result.cards = resolveViberCarouselCards(result.cards, tagValues);
|
|
787
|
+
}
|
|
788
|
+
if (result.viberPreviewContent && typeof result.viberPreviewContent === 'object') {
|
|
789
|
+
const preview = result.viberPreviewContent;
|
|
790
|
+
result.viberPreviewContent = {
|
|
791
|
+
...preview,
|
|
792
|
+
messageContent: resolveTagsInText(preview.messageContent || '', tagValues),
|
|
793
|
+
cards: resolveViberCarouselCards(preview.cards, tagValues),
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
return result;
|
|
797
|
+
};
|
|
798
|
+
|
|
799
|
+
const mergeViberPreviewWithResolved = (base, resolved) => {
|
|
800
|
+
if (!resolved || typeof resolved !== 'object') {
|
|
801
|
+
return base && typeof base === 'object' ? base : {};
|
|
802
|
+
}
|
|
803
|
+
const baseObj = base && typeof base === 'object' ? base : {};
|
|
804
|
+
const basePreview = baseObj.viberPreviewContent || {};
|
|
805
|
+
const resolvedPreview = resolved.viberPreviewContent || {};
|
|
806
|
+
const baseCards = basePreview.cards || baseObj.cards || [];
|
|
807
|
+
const resolvedCards = resolvedPreview.cards || resolved.cards || [];
|
|
808
|
+
const mergedCards = baseCards.length
|
|
809
|
+
? baseCards.map((card, index) => {
|
|
810
|
+
const resolvedCard = resolvedCards[index];
|
|
811
|
+
if (!resolvedCard) {
|
|
812
|
+
return card;
|
|
813
|
+
}
|
|
814
|
+
return {
|
|
815
|
+
...card,
|
|
816
|
+
...(resolvedCard.text != null ? { text: resolvedCard.text } : {}),
|
|
817
|
+
...(resolvedCard.mediaUrl != null ? { mediaUrl: resolvedCard.mediaUrl } : {}),
|
|
818
|
+
buttons: (card.buttons || []).map((button, buttonIndex) => ({
|
|
819
|
+
...button,
|
|
820
|
+
...(resolvedCard.buttons?.[buttonIndex] || {}),
|
|
821
|
+
})),
|
|
822
|
+
};
|
|
823
|
+
})
|
|
824
|
+
: resolvedCards;
|
|
825
|
+
const resolvedMessage = resolved.messageContent ?? resolvedPreview.messageContent;
|
|
826
|
+
|
|
827
|
+
return {
|
|
828
|
+
...baseObj,
|
|
829
|
+
...resolved,
|
|
830
|
+
viberPreviewContent: {
|
|
831
|
+
...basePreview,
|
|
832
|
+
...resolvedPreview,
|
|
833
|
+
type: resolvedPreview.type || basePreview.type || baseObj.type || (mergedCards.length ? MEDIA_TYPE_CAROUSEL : ''),
|
|
834
|
+
cards: mergedCards.length ? mergedCards : (resolvedPreview.cards || basePreview.cards),
|
|
835
|
+
showCarouselEditorPreview: Boolean(
|
|
836
|
+
basePreview.showCarouselEditorPreview
|
|
837
|
+
|| resolvedPreview.showCarouselEditorPreview
|
|
838
|
+
|| mergedCards.length > 0,
|
|
839
|
+
),
|
|
840
|
+
...(resolvedMessage != null ? { messageContent: resolvedMessage } : {}),
|
|
841
|
+
},
|
|
842
|
+
};
|
|
843
|
+
};
|
|
844
|
+
|
|
690
845
|
/**
|
|
691
846
|
* Common handler for saving test customers (both new and existing)
|
|
692
847
|
*/
|
|
@@ -965,11 +1120,13 @@ const CommonTestAndPreview = (props) => {
|
|
|
965
1120
|
templateContent: formDataObj?.bodyText || contentStr,
|
|
966
1121
|
};
|
|
967
1122
|
|
|
968
|
-
case CHANNELS.VIBER:
|
|
1123
|
+
case CHANNELS.VIBER: {
|
|
1124
|
+
const viberFormData = getViberMergedFormData(formDataObj);
|
|
969
1125
|
return {
|
|
970
|
-
templateSubject:
|
|
971
|
-
templateContent: contentStr,
|
|
1126
|
+
templateSubject: viberFormData?.messageTitle || '',
|
|
1127
|
+
templateContent: getViberTagExtractionContent(viberFormData, contentStr),
|
|
972
1128
|
};
|
|
1129
|
+
}
|
|
973
1130
|
|
|
974
1131
|
case CHANNELS.WEBPUSH:
|
|
975
1132
|
return {
|
|
@@ -1433,6 +1590,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
1433
1590
|
// Build templateConfigs
|
|
1434
1591
|
const templateConfigs = {
|
|
1435
1592
|
_id: templateId || '',
|
|
1593
|
+
templateId: formDataObj?.templateId || '',
|
|
1436
1594
|
name: templateNameValue,
|
|
1437
1595
|
template: templateEditorValue,
|
|
1438
1596
|
varMapped,
|
|
@@ -2023,11 +2181,16 @@ const CommonTestAndPreview = (props) => {
|
|
|
2023
2181
|
// 1. formDataObj from getTemplateContent (contains both viberPreviewContent and payload fields)
|
|
2024
2182
|
// 2. formDataObj[0] (array format - legacy)
|
|
2025
2183
|
// 3. contentStr (direct string - legacy)
|
|
2184
|
+
// Merge content prop so listing / preview-only flows include carousel card tags
|
|
2185
|
+
const viberFormData = getViberMergedFormData(formDataObj);
|
|
2186
|
+
formDataObj = viberFormData;
|
|
2026
2187
|
|
|
2027
2188
|
let messageText = '';
|
|
2028
2189
|
let imageData = null;
|
|
2029
2190
|
let videoData = null;
|
|
2030
2191
|
let buttonData = null;
|
|
2192
|
+
let cardsData = [];
|
|
2193
|
+
let messageType = '';
|
|
2031
2194
|
let accountId = null;
|
|
2032
2195
|
let accountDetails = null;
|
|
2033
2196
|
let scenarioKey = '';
|
|
@@ -2042,6 +2205,8 @@ const CommonTestAndPreview = (props) => {
|
|
|
2042
2205
|
imageData = formDataObj.image || null;
|
|
2043
2206
|
videoData = formDataObj.video || null;
|
|
2044
2207
|
buttonData = formDataObj.button || null;
|
|
2208
|
+
cardsData = formDataObj.cards || [];
|
|
2209
|
+
messageType = formDataObj.type || '';
|
|
2045
2210
|
accountId = formDataObj.accountId || null;
|
|
2046
2211
|
accountDetails = formDataObj.accountDetails || null;
|
|
2047
2212
|
scenarioKey = formDataObj.scenarioKey || VIBER_API_SCENARIO_KEY;
|
|
@@ -2068,6 +2233,8 @@ const CommonTestAndPreview = (props) => {
|
|
|
2068
2233
|
url: formDataObj?.buttonURL || '',
|
|
2069
2234
|
};
|
|
2070
2235
|
}
|
|
2236
|
+
cardsData = viberPreview.cards || formDataObj?.cards || [];
|
|
2237
|
+
messageType = viberPreview.type || formDataObj?.type || '';
|
|
2071
2238
|
// Extract account info from parent formDataObj if available
|
|
2072
2239
|
accountId = formDataObj.accountId || null;
|
|
2073
2240
|
accountDetails = formDataObj.accountDetails || null;
|
|
@@ -2110,6 +2277,10 @@ const CommonTestAndPreview = (props) => {
|
|
|
2110
2277
|
text: messageText,
|
|
2111
2278
|
};
|
|
2112
2279
|
|
|
2280
|
+
if (messageType === CHANNELS.VIBER) {
|
|
2281
|
+
messageType = '';
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2113
2284
|
// Add image if present
|
|
2114
2285
|
if (imageData && imageData.url) {
|
|
2115
2286
|
viberContent.image = {
|
|
@@ -2138,9 +2309,34 @@ const CommonTestAndPreview = (props) => {
|
|
|
2138
2309
|
}
|
|
2139
2310
|
}
|
|
2140
2311
|
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
viberContent.
|
|
2312
|
+
if (messageType === MEDIA_TYPE_CAROUSEL || (Array.isArray(cardsData) && cardsData.length)) {
|
|
2313
|
+
viberContent.type = MEDIA_TYPE_CAROUSEL;
|
|
2314
|
+
viberContent.cards = (cardsData || []).map((card) => ({
|
|
2315
|
+
text: card?.text || '',
|
|
2316
|
+
mediaUrl: card?.mediaUrl || '',
|
|
2317
|
+
buttons: (card?.buttons || []).map((button) => ({
|
|
2318
|
+
title: button?.title || '',
|
|
2319
|
+
action: button?.action || '',
|
|
2320
|
+
})),
|
|
2321
|
+
}));
|
|
2322
|
+
delete viberContent.button;
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
// Resolve tags in text/cards if custom values are provided
|
|
2326
|
+
if (customValuesObj && Object.keys(customValuesObj).length > 0) {
|
|
2327
|
+
if (viberContent.text) {
|
|
2328
|
+
viberContent.text = resolveTagsInText(viberContent.text, customValuesObj);
|
|
2329
|
+
}
|
|
2330
|
+
if (Array.isArray(viberContent.cards)) {
|
|
2331
|
+
viberContent.cards = resolveViberCarouselCards(viberContent.cards, customValuesObj);
|
|
2332
|
+
}
|
|
2333
|
+
if (viberContent.button) {
|
|
2334
|
+
viberContent.button = {
|
|
2335
|
+
...viberContent.button,
|
|
2336
|
+
text: resolveTagsInText(viberContent.button.text || '', customValuesObj),
|
|
2337
|
+
url: resolveTagsInText(viberContent.button.url || '', customValuesObj),
|
|
2338
|
+
};
|
|
2339
|
+
}
|
|
2144
2340
|
}
|
|
2145
2341
|
|
|
2146
2342
|
// Build messageBody JSON string
|
|
@@ -2532,33 +2728,39 @@ const CommonTestAndPreview = (props) => {
|
|
|
2532
2728
|
// Only use previewDataHtml if preview call was made
|
|
2533
2729
|
if (hasPreviewCallBeenMade && previewDataHtml?.resolvedBody) {
|
|
2534
2730
|
resolvedContent = previewDataHtml.resolvedBody;
|
|
2731
|
+
if (typeof resolvedContent === 'string') {
|
|
2732
|
+
try {
|
|
2733
|
+
resolvedContent = JSON.parse(resolvedContent);
|
|
2734
|
+
} catch (e) {
|
|
2735
|
+
resolvedContent = null;
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2535
2738
|
}
|
|
2536
2739
|
|
|
2537
|
-
// Parse content if it's a string
|
|
2538
|
-
let parsedViberContent =
|
|
2539
|
-
if (typeof
|
|
2540
|
-
|
|
2541
|
-
parsedViberContent = JSON.parse(content);
|
|
2542
|
-
} catch (e) {
|
|
2543
|
-
parsedViberContent = {};
|
|
2544
|
-
}
|
|
2740
|
+
// Parse content if it's a string, then merge with formData for carousel + tags in all entry flows
|
|
2741
|
+
let parsedViberContent = getViberMergedFormData();
|
|
2742
|
+
if (!parsedViberContent || typeof parsedViberContent !== 'object') {
|
|
2743
|
+
parsedViberContent = {};
|
|
2545
2744
|
}
|
|
2546
|
-
//
|
|
2745
|
+
// Merge template preview state with API resolved body so carousel type/cards survive slim responses
|
|
2547
2746
|
if (resolvedContent && typeof resolvedContent === 'object') {
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
if (
|
|
2551
|
-
contentObj.accountName =
|
|
2747
|
+
const base = parsedViberContent && typeof parsedViberContent === 'object' ? parsedViberContent : {};
|
|
2748
|
+
contentObj = mergeViberPreviewWithResolved(base, resolvedContent);
|
|
2749
|
+
if (base.accountName && !contentObj.accountName) {
|
|
2750
|
+
contentObj.accountName = base.accountName;
|
|
2552
2751
|
}
|
|
2553
|
-
if (
|
|
2554
|
-
contentObj.brandName =
|
|
2752
|
+
if (base.brandName && !contentObj.brandName) {
|
|
2753
|
+
contentObj.brandName = base.brandName;
|
|
2555
2754
|
}
|
|
2556
2755
|
} else {
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2756
|
+
contentObj = parsedViberContent && typeof parsedViberContent === 'object' ? parsedViberContent : {};
|
|
2757
|
+
}
|
|
2758
|
+
if (
|
|
2759
|
+
hasPreviewCallBeenMade
|
|
2760
|
+
&& viberPreviewTagValues
|
|
2761
|
+
&& Object.keys(viberPreviewTagValues).length > 0
|
|
2762
|
+
) {
|
|
2763
|
+
contentObj = applyViberCustomValuesToContent(contentObj, viberPreviewTagValues);
|
|
2562
2764
|
}
|
|
2563
2765
|
} else if (channel === CHANNELS.ZALO) {
|
|
2564
2766
|
// For Zalo, content is an object with templatePreviewUrl, templateStatus, etc.
|
|
@@ -2942,6 +3144,11 @@ const CommonTestAndPreview = (props) => {
|
|
|
2942
3144
|
|
|
2943
3145
|
setCustomValues(updatedValues);
|
|
2944
3146
|
|
|
3147
|
+
// Viber: do not auto-resolve tags in preview; user must click Update preview
|
|
3148
|
+
if (channel === CHANNELS.VIBER) {
|
|
3149
|
+
return;
|
|
3150
|
+
}
|
|
3151
|
+
|
|
2945
3152
|
// Update preview with prefilled values (this is a valid preview call trigger)
|
|
2946
3153
|
// For RCS: always dispatch with RCS channel/content so previewDataHtml stays RCS-shaped.
|
|
2947
3154
|
// SMS fallback preview is kept in sync by syncSmsFallbackPreview via smsFallbackPreviewText.
|
|
@@ -3024,6 +3231,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
3024
3231
|
setPreviewDevice(DESKTOP);
|
|
3025
3232
|
setPreviewDataHtml('');
|
|
3026
3233
|
setHasPreviewCallBeenMade(false); // Reset preview call flag
|
|
3234
|
+
setViberPreviewTagValues(null);
|
|
3027
3235
|
setSelectedTestEntities([]);
|
|
3028
3236
|
setBeeContent('');
|
|
3029
3237
|
previousBeeContentRef.current = '';
|
|
@@ -3061,6 +3269,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
3061
3269
|
const handleClearSelection = () => {
|
|
3062
3270
|
setSelectedCustomer(null);
|
|
3063
3271
|
setHasPreviewCallBeenMade(false); // Reset flag when customer is cleared
|
|
3272
|
+
setViberPreviewTagValues(null);
|
|
3064
3273
|
|
|
3065
3274
|
// Clear all preview errors when customer is cleared
|
|
3066
3275
|
actions.clearPreviewErrors();
|
|
@@ -3119,6 +3328,9 @@ const CommonTestAndPreview = (props) => {
|
|
|
3119
3328
|
emptyValues,
|
|
3120
3329
|
selectedCustomer
|
|
3121
3330
|
);
|
|
3331
|
+
if (channel === CHANNELS.VIBER) {
|
|
3332
|
+
setViberPreviewTagValues({ ...emptyValues });
|
|
3333
|
+
}
|
|
3122
3334
|
actions.updatePreviewRequested(payload);
|
|
3123
3335
|
setHasPreviewCallBeenMade(true); // Mark that preview call was made
|
|
3124
3336
|
};
|
|
@@ -3144,6 +3356,9 @@ const CommonTestAndPreview = (props) => {
|
|
|
3144
3356
|
customValues,
|
|
3145
3357
|
selectedCustomer
|
|
3146
3358
|
);
|
|
3359
|
+
if (channel === CHANNELS.VIBER) {
|
|
3360
|
+
setViberPreviewTagValues({ ...customValues });
|
|
3361
|
+
}
|
|
3147
3362
|
await actions.updatePreviewRequested(payload);
|
|
3148
3363
|
|
|
3149
3364
|
await syncSmsFallbackPreview(customValues, selectedCustomer);
|
|
@@ -601,6 +601,38 @@ describe('SmsPreviewContent', () => {
|
|
|
601
601
|
});
|
|
602
602
|
});
|
|
603
603
|
|
|
604
|
+
describe('DLT token highlighting', () => {
|
|
605
|
+
it('wraps `{#…#}` tokens in a highlighted span and leaves plain text as-is', () => {
|
|
606
|
+
const { container } = render(
|
|
607
|
+
<TestWrapper>
|
|
608
|
+
<ComponentToRender {...defaultProps} content="Hi {#alp#}, code {#num#} thanks" />
|
|
609
|
+
</TestWrapper>
|
|
610
|
+
);
|
|
611
|
+
const tokens = container.querySelectorAll('.sms-preview-dlt-token');
|
|
612
|
+
expect(tokens.length).toBe(2);
|
|
613
|
+
expect(tokens[0].textContent).toBe('{#alp#}');
|
|
614
|
+
expect(tokens[1].textContent).toBe('{#num#}');
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
it('renders no token spans when content has no DLT tokens', () => {
|
|
618
|
+
const { container } = render(
|
|
619
|
+
<TestWrapper>
|
|
620
|
+
<ComponentToRender {...defaultProps} content="Plain SMS body" />
|
|
621
|
+
</TestWrapper>
|
|
622
|
+
);
|
|
623
|
+
expect(container.querySelectorAll('.sms-preview-dlt-token').length).toBe(0);
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
it('falls back to noContent placeholder when content is empty', () => {
|
|
627
|
+
render(
|
|
628
|
+
<TestWrapper>
|
|
629
|
+
<ComponentToRender {...defaultProps} content="" />
|
|
630
|
+
</TestWrapper>
|
|
631
|
+
);
|
|
632
|
+
expect(screen.getByText('No content available')).toBeTruthy();
|
|
633
|
+
});
|
|
634
|
+
});
|
|
635
|
+
|
|
604
636
|
describe('PropTypes Validation', () => {
|
|
605
637
|
it('should accept all required props without warnings', () => {
|
|
606
638
|
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|