@capillarytech/creatives-library 9.0.14-beta.0 → 9.0.14
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 +0 -3
- package/package.json +1 -1
- package/services/api.js +10 -0
- package/services/tests/api.test.js +83 -0
- package/utils/common.js +0 -8
- package/v2Components/CommonTestAndPreview/UnifiedPreview/WhatsAppPreviewContent.js +5 -3
- package/v2Components/CommonTestAndPreview/index.js +7 -0
- package/v2Components/FormBuilder/_formBuilder.scss +0 -5
- package/v2Components/FormBuilder/index.js +4479 -41
- package/v2Components/NavigationBar/index.js +27 -0
- package/v2Components/NavigationBar/messages.js +4 -0
- package/v2Components/NavigationBar/tests/index.test.js +19 -0
- package/v2Components/NewCallTask/index.js +6 -1
- package/v2Components/TemplatePreview/index.js +4 -2
- package/v2Containers/Cap/index.js +3 -1
- package/v2Containers/CommunicationFlow/CommunicationFlow.js +130 -20
- package/v2Containers/CommunicationFlow/CommunicationFlow.scss +154 -0
- package/v2Containers/CommunicationFlow/CommunicationFlowCard.js +240 -0
- package/v2Containers/CommunicationFlow/DemoPage.js +47 -0
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +369 -2
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlowCard.test.js +619 -0
- package/v2Containers/CommunicationFlow/Tests/DemoPage.test.js +77 -0
- package/v2Containers/CommunicationFlow/Tests/getContentBody.test.js +933 -0
- package/v2Containers/CommunicationFlow/constants.js +45 -10
- package/v2Containers/CommunicationFlow/index.js +5 -2
- package/v2Containers/CommunicationFlow/messages.js +20 -0
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.js +94 -31
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.scss +14 -11
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +1144 -32
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/extractContentForPreview.js +183 -0
- package/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/CommunicationStrategyStep.js +3 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.js +39 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +6 -2
- package/v2Containers/CommunicationFlow/utils/getContentBody.js +369 -0
- package/v2Containers/CommunicationFlow/utils/getContentBody.scss +19 -0
- package/v2Containers/CommunicationFlow/utils/getEnabledSteps.js +1 -1
- package/v2Containers/CreativesContainer/constants.js +6 -0
- package/v2Containers/CreativesContainer/index.js +68 -1
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +2 -2
- package/v2Containers/Templates/index.js +2 -2
- package/v2Containers/TemplatesV2/index.js +9 -1
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +41 -34
- package/v2Components/FormBuilder/Classic.js +0 -4487
- package/v2Components/FormBuilder/Functional/FormBuilderShell.js +0 -371
- package/v2Components/FormBuilder/Functional/channels/registry.js +0 -17
- package/v2Components/FormBuilder/Functional/channels/sms/buildSubmitPayload.js +0 -9
- package/v2Components/FormBuilder/Functional/channels/sms/config.js +0 -30
- package/v2Components/FormBuilder/Functional/channels/sms/getEditorErrorDescriptor.js +0 -46
- package/v2Components/FormBuilder/Functional/channels/sms/getLiquidContent.js +0 -13
- package/v2Components/FormBuilder/Functional/channels/sms/index.js +0 -22
- package/v2Components/FormBuilder/Functional/channels/sms/tests/getEditorErrorDescriptor.test.js +0 -52
- package/v2Components/FormBuilder/Functional/channels/sms/tests/getLiquidContent.test.js +0 -25
- package/v2Components/FormBuilder/Functional/channels/sms/tests/validate.test.js +0 -87
- package/v2Components/FormBuilder/Functional/channels/sms/validate.js +0 -89
- package/v2Components/FormBuilder/Functional/constants.js +0 -42
- package/v2Components/FormBuilder/Functional/core/schema/fieldRegistry.js +0 -38
- package/v2Components/FormBuilder/Functional/core/schema/initializeFormState.js +0 -85
- package/v2Components/FormBuilder/Functional/core/store/formReducer.js +0 -81
- package/v2Components/FormBuilder/Functional/core/store/selectors.js +0 -30
- package/v2Components/FormBuilder/Functional/core/store/toLegacyFormData.js +0 -91
- package/v2Components/FormBuilder/Functional/index.js +0 -26
- package/v2Components/FormBuilder/Functional/layout/FieldSlot.js +0 -59
- package/v2Components/FormBuilder/Functional/layout/SchemaForm.js +0 -31
- package/v2Components/FormBuilder/Functional/layout/Section.js +0 -116
- package/v2Components/FormBuilder/Functional/renderers/smsRenderers.js +0 -258
- package/v2Components/FormBuilder/Functional/tests/channelRegistry.test.js +0 -21
- package/v2Components/FormBuilder/Functional/tests/fieldRegistry.test.js +0 -65
- package/v2Components/FormBuilder/Functional/tests/fieldSlot.test.js +0 -97
- package/v2Components/FormBuilder/Functional/tests/fixtures/smsParityCases.js +0 -192
- package/v2Components/FormBuilder/Functional/tests/formReducer.test.js +0 -129
- package/v2Components/FormBuilder/Functional/tests/initializeFormState.test.js +0 -132
- package/v2Components/FormBuilder/Functional/tests/schemaForm.test.js +0 -40
- package/v2Components/FormBuilder/Functional/tests/section.test.js +0 -99
- package/v2Components/FormBuilder/Functional/tests/selectors.test.js +0 -67
- package/v2Components/FormBuilder/Functional/tests/sms.crossFlowParity.test.js +0 -155
- package/v2Components/FormBuilder/Functional/tests/sms.liquid.test.js +0 -172
- package/v2Components/FormBuilder/Functional/tests/sms.rollout.test.js +0 -122
- package/v2Components/FormBuilder/Functional/tests/sms.shell.parity.test.js +0 -329
- package/v2Components/FormBuilder/Functional/tests/smsRenderers.test.js +0 -160
- package/v2Components/FormBuilder/Functional/tests/toLegacyFormData.test.js +0 -95
- package/v2Components/FormBuilder/tests/__snapshots__/sms.characterization.test.js.snap +0 -114
- package/v2Components/FormBuilder/tests/entryGate.test.js +0 -106
- package/v2Components/FormBuilder/tests/sms.characterization.test.js +0 -336
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import {
|
|
2
|
+
VIBER, MOBILE_PUSH, MPUSH, SMS, EMAIL, WEBPUSH, ZALO, INAPP, WHATSAPP, RCS,
|
|
3
|
+
TEXT, IMAGE, VIDEO, DOCUMENT, CTA, QUICK_REPLY,
|
|
4
|
+
} from '../../../CreativesContainer/constants';
|
|
5
|
+
import { getWhatsappDocPreview } from '../../../Whatsapp/utils';
|
|
6
|
+
|
|
7
|
+
const extractSmsContent = (templateData) => {
|
|
8
|
+
const smsBody = templateData.messageBody || templateData.smsBody || '';
|
|
9
|
+
return {
|
|
10
|
+
content: smsBody,
|
|
11
|
+
formData: [{ 'sms-editor': smsBody, activeTab: 'base', base: { 'sms-editor': smsBody } }],
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const extractEmailContent = (templateData) => {
|
|
16
|
+
const emailContent = templateData.emailBody || templateData.emailHtml || '';
|
|
17
|
+
const emailSubject = templateData.emailSubject || '';
|
|
18
|
+
const formData = [{ 'template-subject': emailSubject, activeTab: 'base', base: { 'template-content': emailContent } }];
|
|
19
|
+
formData['template-subject'] = emailSubject;
|
|
20
|
+
return { content: emailContent, formData };
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const extractMobilePushDevice = ({
|
|
24
|
+
title = '',
|
|
25
|
+
message = '',
|
|
26
|
+
expandableDetails: {
|
|
27
|
+
image = '', media = [], ctas = [], carouselData = [],
|
|
28
|
+
} = {},
|
|
29
|
+
} = {}) => ({
|
|
30
|
+
header: title,
|
|
31
|
+
bodyText: message,
|
|
32
|
+
bodyImage: image,
|
|
33
|
+
bodyVideo: media[0] || null,
|
|
34
|
+
bodyGif: '',
|
|
35
|
+
actions: ctas,
|
|
36
|
+
carouselData,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const extractMobilePushContent = (templateData) => ({
|
|
40
|
+
content: {
|
|
41
|
+
androidContent: extractMobilePushDevice(templateData.androidContent),
|
|
42
|
+
iosContent: extractMobilePushDevice(templateData.iosContent),
|
|
43
|
+
},
|
|
44
|
+
formData: null,
|
|
45
|
+
accountId: templateData.accountId,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const extractInAppDevice = (deviceData = {}) => {
|
|
49
|
+
const ctas = deviceData.expandableDetails?.ctas || [];
|
|
50
|
+
return {
|
|
51
|
+
mediaPreview: { inAppImageSrcAndroid: deviceData.expandableDetails?.image || undefined },
|
|
52
|
+
templateTitle: deviceData.title || '',
|
|
53
|
+
templateMsg: deviceData.message || '',
|
|
54
|
+
ctaData: ctas,
|
|
55
|
+
templateLayoutType: deviceData.bodyType || '',
|
|
56
|
+
deepLinkValue: ctas[0]?.actionLink || null,
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const extractInAppContent = (templateData) => ({
|
|
61
|
+
content: {
|
|
62
|
+
androidContent: extractInAppDevice(templateData.androidContent),
|
|
63
|
+
iosContent: {
|
|
64
|
+
...extractInAppDevice(templateData.iosContent),
|
|
65
|
+
mediaPreview: { inAppImageSrcIos: templateData.iosContent?.expandableDetails?.image || undefined },
|
|
66
|
+
},
|
|
67
|
+
templateLayoutType: templateData.androidContent?.bodyType || '',
|
|
68
|
+
templateMediaType: templateData.androidContent?.expandableDetails?.image ? IMAGE : TEXT,
|
|
69
|
+
},
|
|
70
|
+
formData: null,
|
|
71
|
+
accountId: templateData.accountId,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const extractWhatsappContent = (templateData) => {
|
|
75
|
+
const configs = templateData.templateConfigs || {};
|
|
76
|
+
const media = configs.whatsappMedia || {};
|
|
77
|
+
const buttonType = configs.buttonType || '';
|
|
78
|
+
const buttons = configs.buttons || [];
|
|
79
|
+
return {
|
|
80
|
+
content: {
|
|
81
|
+
templateMsg: templateData.messageBody || '',
|
|
82
|
+
templateHeader: media.header || '',
|
|
83
|
+
templateFooter: media.footer || '',
|
|
84
|
+
whatsappImageSrc: configs.mediaType === IMAGE ? media.url : '',
|
|
85
|
+
whatsappVideoPreviewImg: configs.mediaType === VIDEO ? media.previewUrl : '',
|
|
86
|
+
docPreview: configs.mediaType === DOCUMENT && media.docParams
|
|
87
|
+
? getWhatsappDocPreview({ ...media.docParams, whatsappDocImg: media.previewUrl || '' })
|
|
88
|
+
: null,
|
|
89
|
+
ctaData: buttonType === CTA ? buttons : [],
|
|
90
|
+
quickReplyData: buttonType === QUICK_REPLY ? buttons : [],
|
|
91
|
+
carouselData: configs.cards || [],
|
|
92
|
+
accountId: templateData.accountId,
|
|
93
|
+
sourceAccountIdentifier: templateData.sourceAccountIdentifier,
|
|
94
|
+
accountName: templateData.accountName,
|
|
95
|
+
},
|
|
96
|
+
formData: null,
|
|
97
|
+
accountId: templateData.accountId,
|
|
98
|
+
sourceAccountIdentifier: templateData.sourceAccountIdentifier,
|
|
99
|
+
accountName: templateData.accountName,
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const parseJson = (value) => {
|
|
104
|
+
try {
|
|
105
|
+
return typeof value === 'string' ? JSON.parse(value || '{}') : (value || {});
|
|
106
|
+
} catch {
|
|
107
|
+
return {};
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const extractViberContent = (templateData) => {
|
|
112
|
+
const parsedBody = parseJson(templateData.messageBody);
|
|
113
|
+
const accountDetails = parseJson(templateData.accountDetails);
|
|
114
|
+
const content = parsedBody.content || parsedBody;
|
|
115
|
+
const accountName = accountDetails?.name || accountDetails?.accountName || '';
|
|
116
|
+
const result = {
|
|
117
|
+
viberPreviewContent: {
|
|
118
|
+
messageContent: content.text || '',
|
|
119
|
+
buttonText: content.button?.text || '',
|
|
120
|
+
imageURL: content.image?.url || undefined,
|
|
121
|
+
},
|
|
122
|
+
accountName: accountName ? [accountName] : [],
|
|
123
|
+
brandName: accountName ? [accountName] : [],
|
|
124
|
+
};
|
|
125
|
+
if (content.video?.url) {
|
|
126
|
+
result.videoParams = {
|
|
127
|
+
viberVideoSrc: content.video.url,
|
|
128
|
+
viberVideoPreviewImg: content.video.thumbnailUrl || '',
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return { content: result, formData: null };
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const extractRcsContent = (templateData) => {
|
|
135
|
+
const cardContent = templateData.rcsContent?.cardContent?.[0] || {};
|
|
136
|
+
const media = cardContent.media || {};
|
|
137
|
+
return {
|
|
138
|
+
content: {
|
|
139
|
+
templateHeader: cardContent.title || '',
|
|
140
|
+
templateMessage: cardContent.description || '',
|
|
141
|
+
rcsImageSrc: media.mediaType === IMAGE ? media.mediaUrl : undefined,
|
|
142
|
+
rcsVideoSrc: media.mediaType === VIDEO ? media.mediaUrl : undefined,
|
|
143
|
+
rcsThumbnailSrc: media.mediaType === VIDEO ? media.thumbnailUrl : undefined,
|
|
144
|
+
suggestions: cardContent.suggestions || [],
|
|
145
|
+
},
|
|
146
|
+
formData: null,
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const extractWebpushContent = (templateData) => ({
|
|
151
|
+
content: templateData?.messageContent?.content || {},
|
|
152
|
+
formData: null,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const extractZaloContent = (templateData) => ({
|
|
156
|
+
content: {
|
|
157
|
+
...templateData,
|
|
158
|
+
templatePreviewUrl: templateData?.templateConfigs?.template || templateData?.templatePreviewUrl || '',
|
|
159
|
+
},
|
|
160
|
+
formData: null,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const CHANNEL_EXTRACTORS = {
|
|
164
|
+
[SMS]: extractSmsContent,
|
|
165
|
+
[EMAIL]: extractEmailContent,
|
|
166
|
+
[MOBILE_PUSH]: extractMobilePushContent,
|
|
167
|
+
[MPUSH]: extractMobilePushContent,
|
|
168
|
+
[INAPP]: extractInAppContent,
|
|
169
|
+
[WHATSAPP]: extractWhatsappContent,
|
|
170
|
+
[VIBER]: extractViberContent,
|
|
171
|
+
[RCS]: extractRcsContent,
|
|
172
|
+
[WEBPUSH]: extractWebpushContent,
|
|
173
|
+
[ZALO]: extractZaloContent,
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const extractContentForPreview = (item) => {
|
|
177
|
+
const templateData = item?.templateData || {};
|
|
178
|
+
const upperChannel = item?.channel?.toUpperCase();
|
|
179
|
+
const extractor = CHANNEL_EXTRACTORS[upperChannel];
|
|
180
|
+
return extractor ? extractor(templateData) : { content: templateData, formData: null };
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
export default extractContentForPreview;
|
package/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/CommunicationStrategyStep.js
CHANGED
|
@@ -40,6 +40,7 @@ const CommunicationStrategyStep = ({
|
|
|
40
40
|
key: option?.value,
|
|
41
41
|
value: option?.value,
|
|
42
42
|
label: customLabel,
|
|
43
|
+
simpleLabel: option?.label,
|
|
43
44
|
disabled: option?.disabled,
|
|
44
45
|
};
|
|
45
46
|
}), [optionsProp, formatMessage]);
|
|
@@ -63,6 +64,8 @@ const CommunicationStrategyStep = ({
|
|
|
63
64
|
onChange={handleChange}
|
|
64
65
|
placeholder={formatMessage(messages.communicationStrategyPlaceholder)}
|
|
65
66
|
options={options}
|
|
67
|
+
propToBeMadeLabel="simpleLabel"
|
|
68
|
+
optionLabelProp="label"
|
|
66
69
|
style={{ width: '20.375rem' }}
|
|
67
70
|
disabled={disabled}
|
|
68
71
|
/>
|
package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.js
CHANGED
|
@@ -174,6 +174,45 @@ const DeliverySettingsSection = ({
|
|
|
174
174
|
return merged;
|
|
175
175
|
}, [domainPropertiesData, wecrmViberData]);
|
|
176
176
|
|
|
177
|
+
// Helper: returns true if a channelSetting entry has at least one real (non-empty) value
|
|
178
|
+
const hasRealData = (settings) => settings && Object.values(settings).some((value) => value !== null && value !== '' && value !== undefined);
|
|
179
|
+
|
|
180
|
+
// When the domain properties API response arrives, auto-save the default sender IDs
|
|
181
|
+
// into channelSetting so they are included in the payload even if the user never
|
|
182
|
+
// opens the SenderDetails slidebox.
|
|
183
|
+
//
|
|
184
|
+
// Rules:
|
|
185
|
+
// - Only run for channels that are currently selected (deliveryChannels)
|
|
186
|
+
// - Only save a channel if the API returned a real sender ID for it (not empty)
|
|
187
|
+
// - Never overwrite a channel the user has already configured manually
|
|
188
|
+
useEffect(() => {
|
|
189
|
+
if (!entityWithWecrmViber || !onDeliverySettingChange || deliveryChannels.length === 0) return;
|
|
190
|
+
|
|
191
|
+
// Convert the API entity into the channelSetting shape (e.g. { SMS: { gsmSenderId: '...' } })
|
|
192
|
+
const senderDetailsFromAPI = parseSenderDetailsFromEntity(entityWithWecrmViber, { whatsappSourceAccountId });
|
|
193
|
+
const channelSettingFromAPI = buildChannelSettingFromFieldValues(senderDetailsFromAPI);
|
|
194
|
+
|
|
195
|
+
const savedChannelSetting = deliverySetting?.channelSetting || {};
|
|
196
|
+
const updatedChannelSetting = { ...savedChannelSetting };
|
|
197
|
+
let didChange = false;
|
|
198
|
+
|
|
199
|
+
deliveryChannels.forEach((channel) => {
|
|
200
|
+
const apiDataForChannel = channelSettingFromAPI[channel];
|
|
201
|
+
const userSavedDataForChannel = savedChannelSetting[channel];
|
|
202
|
+
|
|
203
|
+
const apiHasRealSenderId = hasRealData(apiDataForChannel);
|
|
204
|
+
const userAlreadyConfigured = hasRealData(userSavedDataForChannel);
|
|
205
|
+
|
|
206
|
+
if (apiHasRealSenderId && !userAlreadyConfigured) {
|
|
207
|
+
updatedChannelSetting[channel] = apiDataForChannel;
|
|
208
|
+
didChange = true;
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
if (!didChange) return;
|
|
213
|
+
onDeliverySettingChange({ ...deliverySetting, channelSetting: updatedChannelSetting });
|
|
214
|
+
}, [entityWithWecrmViber]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
215
|
+
|
|
177
216
|
const parsedSenderDetails = useMemo(() => {
|
|
178
217
|
if (!entityWithWecrmViber) return null;
|
|
179
218
|
const fromEntity = parseSenderDetailsFromEntity(entityWithWecrmViber, { whatsappSourceAccountId });
|
|
@@ -278,10 +278,10 @@ describe('DeliverySettingsSection — marketer flows', () => {
|
|
|
278
278
|
|
|
279
279
|
await waitFor(() => {
|
|
280
280
|
expect(screen.getByText('zalo-sender-77')).toBeInTheDocument();
|
|
281
|
-
expect(screen.getByText('Account')).toBeInTheDocument();
|
|
282
|
-
expect(screen.getByText('line@account-id')).toBeInTheDocument();
|
|
283
281
|
expect(screen.getByText('+12025550123')).toBeInTheDocument();
|
|
284
282
|
});
|
|
283
|
+
// LINE is excluded from delivery settings (CHANNELS_WITHOUT_DELIVERY), so no account row
|
|
284
|
+
expect(screen.queryByText('line@account-id')).not.toBeInTheDocument();
|
|
285
285
|
});
|
|
286
286
|
});
|
|
287
287
|
|
|
@@ -538,6 +538,10 @@ describe('DeliverySettingsSection — marketer flows', () => {
|
|
|
538
538
|
});
|
|
539
539
|
|
|
540
540
|
await waitFor(() => expect(screen.getByText('Sender details')).toBeInTheDocument());
|
|
541
|
+
// The auto-save effect fires when domainProperties loads, calling onDeliverySettingChange
|
|
542
|
+
// with the API default (+1002003001). Wait for it, then clear so only the manual save is checked.
|
|
543
|
+
await waitFor(() => expect(onDeliverySettingChange).toHaveBeenCalled());
|
|
544
|
+
onDeliverySettingChange.mockClear();
|
|
541
545
|
await openSenderDetailsFromSummary();
|
|
542
546
|
|
|
543
547
|
const slidebox = document.querySelector('.sender-details');
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import CapImage from '@capillarytech/cap-ui-library/CapImage';
|
|
3
|
+
import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
|
|
4
|
+
import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
|
|
5
|
+
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
6
|
+
import { CAP_SPACE_04 } from '@capillarytech/cap-ui-library/styled/variables';
|
|
7
|
+
import { getWhatsappDocPreview, getWhatsappQuickReply, getWhatsappCarouselButtonView } from '../../Whatsapp/utils';
|
|
8
|
+
import { PHONE_NUMBER } from '../../Whatsapp/constants';
|
|
9
|
+
import {
|
|
10
|
+
VIBER, MOBILE_PUSH, MPUSH, SMS, EMAIL, INAPP, WEBPUSH, RCS, ZALO, WHATSAPP, LINE, IMAGE, VIDEO, DOCUMENT, CTA,
|
|
11
|
+
} from '../../CreativesContainer/constants';
|
|
12
|
+
import videoPlay from '../../../assets/videoPlay.svg';
|
|
13
|
+
import './getContentBody.scss';
|
|
14
|
+
|
|
15
|
+
const getContentBody = (item) => {
|
|
16
|
+
const templateData = item?.templateData || {};
|
|
17
|
+
const channel = item?.channel?.toUpperCase();
|
|
18
|
+
|
|
19
|
+
switch (channel) {
|
|
20
|
+
case SMS: {
|
|
21
|
+
const smsText = templateData.messageBody || templateData.smsBody || '';
|
|
22
|
+
return <CapLabel type="label2">{smsText}</CapLabel>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
case EMAIL: {
|
|
26
|
+
return <CapLabel type="label2">{templateData.emailSubject || ''}</CapLabel>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
case MOBILE_PUSH:
|
|
30
|
+
case MPUSH: {
|
|
31
|
+
const deviceData = templateData.androidContent || templateData.iosContent || {};
|
|
32
|
+
const title = deviceData.title || '';
|
|
33
|
+
const message = deviceData.message || '';
|
|
34
|
+
const image = deviceData.expandableDetails?.image || '';
|
|
35
|
+
const media = deviceData.expandableDetails?.media?.[0] || null;
|
|
36
|
+
const ctas = deviceData.expandableDetails?.ctas || [];
|
|
37
|
+
const carouselData = deviceData.expandableDetails?.carouselData || [];
|
|
38
|
+
return (
|
|
39
|
+
<CapRow className="mobilepush-container">
|
|
40
|
+
<CapRow className="app-header">
|
|
41
|
+
<CapRow className="app-header-left">
|
|
42
|
+
<CapRow className="app-icon" />
|
|
43
|
+
<CapLabel type="label4">{title}</CapLabel>
|
|
44
|
+
</CapRow>
|
|
45
|
+
</CapRow>
|
|
46
|
+
<CapLabel className="mobilepush-message" type="label1">{message}</CapLabel>
|
|
47
|
+
{image && !media && !carouselData.length && (
|
|
48
|
+
<CapImage src={image} className="mobilepush-image mobilepush-image-padding" />
|
|
49
|
+
)}
|
|
50
|
+
{media?.url && (
|
|
51
|
+
<CapRow className="video-preview">
|
|
52
|
+
<video controls={false} tabIndex="0">
|
|
53
|
+
<source src={media.url} type="video/mp4" />
|
|
54
|
+
</video>
|
|
55
|
+
<CapRow className="icon-position">
|
|
56
|
+
<CapImage className="video-icon" src={videoPlay} />
|
|
57
|
+
</CapRow>
|
|
58
|
+
</CapRow>
|
|
59
|
+
)}
|
|
60
|
+
{carouselData.length > 0 && (
|
|
61
|
+
<CapRow className="scroll-container">
|
|
62
|
+
{carouselData.map((data, index) => (
|
|
63
|
+
<CapRow key={`carousel-${index}`} className="whatsapp-carousel-container" role="group">
|
|
64
|
+
<CapRow className="whatsapp-carousel-card">
|
|
65
|
+
{data?.imageUrl && (
|
|
66
|
+
<CapImage src={data?.imageUrl} className="whatsapp-image" />
|
|
67
|
+
)}
|
|
68
|
+
</CapRow>
|
|
69
|
+
</CapRow>
|
|
70
|
+
))}
|
|
71
|
+
</CapRow>
|
|
72
|
+
)}
|
|
73
|
+
{ctas.length > 0 && (
|
|
74
|
+
<CapRow className="actions">
|
|
75
|
+
{ctas.map((cta) => (
|
|
76
|
+
<CapLabel className="action" key={cta?.actionText}>
|
|
77
|
+
{cta?.actionText?.toUpperCase()}
|
|
78
|
+
</CapLabel>
|
|
79
|
+
))}
|
|
80
|
+
</CapRow>
|
|
81
|
+
)}
|
|
82
|
+
</CapRow>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
case INAPP: {
|
|
87
|
+
const deviceData = templateData.androidContent || templateData.iosContent || {};
|
|
88
|
+
const title = deviceData.title || '';
|
|
89
|
+
const message = deviceData.message || '';
|
|
90
|
+
const image = deviceData.expandableDetails?.image || '';
|
|
91
|
+
const ctas = deviceData.expandableDetails?.ctas || [];
|
|
92
|
+
return (
|
|
93
|
+
<>
|
|
94
|
+
{title && <CapLabel type="label2">{title}</CapLabel>}
|
|
95
|
+
{message && <CapLabel type="label1">{message}</CapLabel>}
|
|
96
|
+
{image && <CapImage src={image} className="mobilepush-image" />}
|
|
97
|
+
{ctas.length > 0 && (
|
|
98
|
+
<CapRow className="actions">
|
|
99
|
+
{ctas.map((cta) => (
|
|
100
|
+
<CapLabel className="action" key={cta?.actionText}>
|
|
101
|
+
{cta?.actionText?.toUpperCase()}
|
|
102
|
+
</CapLabel>
|
|
103
|
+
))}
|
|
104
|
+
</CapRow>
|
|
105
|
+
)}
|
|
106
|
+
</>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
case WEBPUSH: {
|
|
111
|
+
const innerContent = templateData?.messageContent?.content?.content || {};
|
|
112
|
+
const notificationTitle = innerContent.title || '';
|
|
113
|
+
const notificationMessage = innerContent.message || '';
|
|
114
|
+
const iconImageUrl = innerContent.iconImageUrl || '';
|
|
115
|
+
const mediaImage = innerContent.expandableDetails?.image || '';
|
|
116
|
+
const rawCtas = innerContent.cta ? [innerContent.cta] : (innerContent.ctas || []);
|
|
117
|
+
const visibleCtas = rawCtas.slice(0, 2);
|
|
118
|
+
return (
|
|
119
|
+
<CapRow className="sms-template-content webpush-template-content">
|
|
120
|
+
<CapRow className="webpush-template-card">
|
|
121
|
+
<CapRow className="webpush-template-meta">
|
|
122
|
+
{iconImageUrl
|
|
123
|
+
? <CapImage src={iconImageUrl} alt="Brand Icon" className="webpush-brand-icon" />
|
|
124
|
+
: <CapRow className="webpush-brand-icon-placeholder" />
|
|
125
|
+
}
|
|
126
|
+
<CapRow className="webpush-template-text">
|
|
127
|
+
<CapRow className="webpush-template-header">
|
|
128
|
+
<CapLabel className="webpush-template-title">{notificationTitle}</CapLabel>
|
|
129
|
+
</CapRow>
|
|
130
|
+
{notificationMessage && (
|
|
131
|
+
<CapLabel className="webpush-template-message">{notificationMessage}</CapLabel>
|
|
132
|
+
)}
|
|
133
|
+
{mediaImage && (
|
|
134
|
+
<CapImage src={mediaImage} alt="Media" className="webpush-media-image" />
|
|
135
|
+
)}
|
|
136
|
+
</CapRow>
|
|
137
|
+
</CapRow>
|
|
138
|
+
{visibleCtas.length > 0 && (
|
|
139
|
+
<CapRow className={`webpush-template-cta-section${visibleCtas.length === 1 ? ' single-cta' : ''}`}>
|
|
140
|
+
{visibleCtas.map((cta, index) => (
|
|
141
|
+
<CapRow className="webpush-template-cta-item" key={`webpush-cta-${index}`}>
|
|
142
|
+
<CapLabel className="webpush-template-cta-text">{cta.text || cta.actionText}</CapLabel>
|
|
143
|
+
</CapRow>
|
|
144
|
+
))}
|
|
145
|
+
</CapRow>
|
|
146
|
+
)}
|
|
147
|
+
</CapRow>
|
|
148
|
+
</CapRow>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
case VIBER: {
|
|
153
|
+
let content = {};
|
|
154
|
+
try {
|
|
155
|
+
const parsed = typeof templateData.messageBody === 'string'
|
|
156
|
+
? JSON.parse(templateData.messageBody || '{}')
|
|
157
|
+
: (templateData.messageBody || {});
|
|
158
|
+
content = parsed.content || parsed;
|
|
159
|
+
} catch {
|
|
160
|
+
content = {};
|
|
161
|
+
}
|
|
162
|
+
const { text = '', image = {}, button = {}, video = {} } = content;
|
|
163
|
+
return (
|
|
164
|
+
<>
|
|
165
|
+
{text && <CapLabel type="label1">{text}</CapLabel>}
|
|
166
|
+
{image?.url && (
|
|
167
|
+
<CapImage src={image.url} className="viber-image" />
|
|
168
|
+
)}
|
|
169
|
+
{video?.url && (
|
|
170
|
+
<CapRow className="video-preview">
|
|
171
|
+
<CapImage src={video.thumbnailUrl || video.url} className="viber-image" />
|
|
172
|
+
<CapRow className="icon-position">
|
|
173
|
+
<CapImage className="video-icon" src={videoPlay} />
|
|
174
|
+
</CapRow>
|
|
175
|
+
</CapRow>
|
|
176
|
+
)}
|
|
177
|
+
{button?.text && (
|
|
178
|
+
<CapRow className="button-content viber-button-content">{button.text}</CapRow>
|
|
179
|
+
)}
|
|
180
|
+
</>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
case WHATSAPP: {
|
|
185
|
+
const configs = templateData.templateConfigs || {};
|
|
186
|
+
const media = configs.whatsappMedia || {};
|
|
187
|
+
const mediaType = configs.mediaType || '';
|
|
188
|
+
const buttonType = configs.buttonType || '';
|
|
189
|
+
const buttons = configs.buttons || [];
|
|
190
|
+
const carouselData = configs.cards || [];
|
|
191
|
+
const bodyText = templateData.messageBody || '';
|
|
192
|
+
const header = media.header || '';
|
|
193
|
+
const footer = media.footer || '';
|
|
194
|
+
const imageUrl = mediaType === IMAGE ? media.url : '';
|
|
195
|
+
const videoPreviewImg = mediaType === VIDEO ? media.previewUrl : '';
|
|
196
|
+
const docPreview = mediaType === DOCUMENT && media.docParams
|
|
197
|
+
? getWhatsappDocPreview({ ...media.docParams, whatsappDocImg: media.previewUrl || '' })
|
|
198
|
+
: null;
|
|
199
|
+
const hasMedia = imageUrl || videoPreviewImg || docPreview;
|
|
200
|
+
return (
|
|
201
|
+
<>
|
|
202
|
+
<CapRow className="whatsapp-container">
|
|
203
|
+
{imageUrl && <CapImage src={imageUrl} className="whatsapp-image" />}
|
|
204
|
+
{videoPreviewImg && (
|
|
205
|
+
<CapRow className="video-preview">
|
|
206
|
+
<CapImage src={videoPreviewImg} className="whatsapp-image" />
|
|
207
|
+
<CapRow className="icon-position">
|
|
208
|
+
<CapImage className="video-icon" src={videoPlay} />
|
|
209
|
+
</CapRow>
|
|
210
|
+
</CapRow>
|
|
211
|
+
)}
|
|
212
|
+
{docPreview && <CapRow className="whatsapp-doc">{docPreview}</CapRow>}
|
|
213
|
+
<CapRow className={hasMedia ? 'whatsapp-message-with-media' : 'whatsapp-message-without-media'}>
|
|
214
|
+
{header && <CapLabel className="whatsapp-template-list-header-preview">{header}</CapLabel>}
|
|
215
|
+
<CapLabel type="label9">{bodyText}</CapLabel>
|
|
216
|
+
{footer && <CapLabel className="whatsapp-template-list-footer-preview">{footer}</CapLabel>}
|
|
217
|
+
</CapRow>
|
|
218
|
+
{buttonType === CTA && buttons.map((cta) => cta.text && (
|
|
219
|
+
<CapLabel type="label21" className="whatsapp-cta-label" key={cta.text}>
|
|
220
|
+
<CapIcon
|
|
221
|
+
type={cta.type === PHONE_NUMBER ? 'call' : 'launch'}
|
|
222
|
+
size="xs"
|
|
223
|
+
svgProps={{ style: { marginRight: CAP_SPACE_04 } }}
|
|
224
|
+
/>
|
|
225
|
+
{cta.text}
|
|
226
|
+
</CapLabel>
|
|
227
|
+
))}
|
|
228
|
+
{getWhatsappQuickReply({ buttonType, buttons }, true)}
|
|
229
|
+
</CapRow>
|
|
230
|
+
{carouselData.length > 0 && (
|
|
231
|
+
<CapRow className="scroll-container">
|
|
232
|
+
{carouselData.map((data, index) => (
|
|
233
|
+
<CapRow key={`carousel-${index}`} className="whatsapp-carousel-container" role="group">
|
|
234
|
+
<CapRow className="whatsapp-carousel-card">
|
|
235
|
+
{data?.mediaType?.toUpperCase() === IMAGE && (
|
|
236
|
+
<CapImage src={data?.imageUrl} className="whatsapp-image" />
|
|
237
|
+
)}
|
|
238
|
+
{data?.mediaType?.toUpperCase() === VIDEO && (
|
|
239
|
+
<CapRow className="video-preview">
|
|
240
|
+
<CapImage src={data?.videoPreviewImg} className="whatsapp-image" />
|
|
241
|
+
<CapRow className="icon-position">
|
|
242
|
+
<CapImage className="video-icon" src={videoPlay} />
|
|
243
|
+
</CapRow>
|
|
244
|
+
</CapRow>
|
|
245
|
+
)}
|
|
246
|
+
<CapRow className={data?.imageUrl || data?.videoPreviewImg ? 'whatsapp-message-with-media' : 'whatsapp-message-without-media'}>
|
|
247
|
+
<CapLabel type="label9" className="whatsapp-carousel-body">{data?.bodyText}</CapLabel>
|
|
248
|
+
</CapRow>
|
|
249
|
+
{getWhatsappCarouselButtonView(data?.buttons, false)}
|
|
250
|
+
</CapRow>
|
|
251
|
+
</CapRow>
|
|
252
|
+
))}
|
|
253
|
+
</CapRow>
|
|
254
|
+
)}
|
|
255
|
+
</>
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
case RCS: {
|
|
260
|
+
const cardContent = templateData.rcsContent?.cardContent?.[0] || {};
|
|
261
|
+
const media = cardContent.media || {};
|
|
262
|
+
const title = cardContent.title || '';
|
|
263
|
+
const description = cardContent.description || '';
|
|
264
|
+
const suggestions = cardContent.suggestions || [];
|
|
265
|
+
return (
|
|
266
|
+
<>
|
|
267
|
+
{media.mediaType === IMAGE && (
|
|
268
|
+
<CapImage src={media.mediaUrl} className="rcs-image" />
|
|
269
|
+
)}
|
|
270
|
+
{media.mediaType === VIDEO && (
|
|
271
|
+
<CapRow className="video-preview">
|
|
272
|
+
<CapImage src={media.thumbnailUrl || media.mediaUrl} className="rcs-image" />
|
|
273
|
+
<CapRow className="icon-position">
|
|
274
|
+
<CapImage className="video-icon" src={videoPlay} />
|
|
275
|
+
</CapRow>
|
|
276
|
+
</CapRow>
|
|
277
|
+
)}
|
|
278
|
+
{title && <CapLabel type="label4">{title}</CapLabel>}
|
|
279
|
+
{description && <CapLabel type="label1">{description}</CapLabel>}
|
|
280
|
+
{suggestions.length > 0 && (
|
|
281
|
+
<CapRow className="actions">
|
|
282
|
+
{suggestions.map((s) => (
|
|
283
|
+
<CapLabel className="action" key={s.label || s.text || s.postback?.data}>{s.label || s.text}</CapLabel>
|
|
284
|
+
))}
|
|
285
|
+
</CapRow>
|
|
286
|
+
)}
|
|
287
|
+
</>
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
case ZALO: {
|
|
292
|
+
// templateData = { ...createPayload() } — name is nested under templateConfigs.name
|
|
293
|
+
const zaloName = templateData?.templateConfigs?.name || '';
|
|
294
|
+
return (
|
|
295
|
+
<CapLabel type="label19" className="zalo-listing-content desc">
|
|
296
|
+
{zaloName}
|
|
297
|
+
</CapLabel>
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
case LINE: {
|
|
302
|
+
let lineContent = {};
|
|
303
|
+
try {
|
|
304
|
+
lineContent = typeof templateData.messageBody === 'string'
|
|
305
|
+
? JSON.parse(templateData.messageBody || '{}')
|
|
306
|
+
: (templateData.messageBody || {});
|
|
307
|
+
} catch {
|
|
308
|
+
lineContent = {};
|
|
309
|
+
}
|
|
310
|
+
const [firstMessage = {}] = lineContent.messages || [];
|
|
311
|
+
const { type: msgType = '' } = firstMessage;
|
|
312
|
+
|
|
313
|
+
if (msgType === 'flex') {
|
|
314
|
+
const flexContents = firstMessage.contents?.contents || [];
|
|
315
|
+
const heroUrl = flexContents[0]?.hero?.url || firstMessage.contents?.hero?.url || '';
|
|
316
|
+
return heroUrl ? <CapImage src={heroUrl} className="line-image" /> : null;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (msgType === 'template') {
|
|
320
|
+
const columns = firstMessage.template?.columns || [];
|
|
321
|
+
return (
|
|
322
|
+
<CapRow>
|
|
323
|
+
{columns.map(({ imageUrl, action: { label: actionLabel } = {} }, idx) => (
|
|
324
|
+
<CapImage
|
|
325
|
+
key={`${imageUrl}_${idx}`}
|
|
326
|
+
src={imageUrl}
|
|
327
|
+
alt={actionLabel}
|
|
328
|
+
className="line-template-image"
|
|
329
|
+
/>
|
|
330
|
+
))}
|
|
331
|
+
</CapRow>
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (msgType === 'video') {
|
|
336
|
+
const videoUrl = firstMessage.video?.originalContentUrl || '';
|
|
337
|
+
return videoUrl ? (
|
|
338
|
+
<CapRow className="video-preview">
|
|
339
|
+
<video controls={false} tabIndex="0">
|
|
340
|
+
<source src={videoUrl} type="video/mp4" />
|
|
341
|
+
</video>
|
|
342
|
+
<CapRow className="icon-position">
|
|
343
|
+
<CapImage className="video-icon" src={videoPlay} />
|
|
344
|
+
</CapRow>
|
|
345
|
+
</CapRow>
|
|
346
|
+
) : null;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// image / sticker / imagemap
|
|
350
|
+
const imageUrl = firstMessage.originalContentUrl
|
|
351
|
+
|| firstMessage.animatedStickerUrl
|
|
352
|
+
|| firstMessage.stickerUrl
|
|
353
|
+
|| (firstMessage.baseUrl ? `${firstMessage.baseUrl}/1040` : '');
|
|
354
|
+
if (imageUrl) {
|
|
355
|
+
return <CapImage src={imageUrl} className="line-image" />;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// text
|
|
359
|
+
return firstMessage.text
|
|
360
|
+
? <CapLabel type="label2">{firstMessage.text}</CapLabel>
|
|
361
|
+
: null;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
default:
|
|
365
|
+
return <CapLabel type="label2">{templateData.messageBody || templateData.message || item.channel || ''}</CapLabel>;
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
export default getContentBody;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
@import '~@capillarytech/cap-ui-library/styles/variables';
|
|
2
|
+
|
|
3
|
+
.viber-button-content {
|
|
4
|
+
margin-top: 0.5rem;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.whatsapp-cta-label {
|
|
8
|
+
text-align: center;
|
|
9
|
+
margin-top: 0.75rem;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.whatsapp-cta-icon {
|
|
13
|
+
margin-right: 0.25rem;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.line-template-image {
|
|
17
|
+
width: 13rem;
|
|
18
|
+
height: 17.4375rem;
|
|
19
|
+
}
|
|
@@ -23,7 +23,7 @@ export const getEnabledSteps = (config) => {
|
|
|
23
23
|
if (features.deliverySettingsData?.required) {
|
|
24
24
|
steps.push(STEPS.DELIVERY_SETTINGS);
|
|
25
25
|
}
|
|
26
|
-
if (features.
|
|
26
|
+
if (features.showDynamicControls) {
|
|
27
27
|
steps.push(STEPS.DYNAMIC_CONTROLS);
|
|
28
28
|
}
|
|
29
29
|
return steps;
|
|
@@ -43,6 +43,12 @@ export const LIQUID_SUPPORTED_CHANNELS = [EMAIL, SMS, MOBILE_PUSH, INAPP];
|
|
|
43
43
|
export const EMF = "EMF";
|
|
44
44
|
export const VENENO = "VENENO";
|
|
45
45
|
export const TEXT = "TEXT";
|
|
46
|
+
export const IMAGE = "IMAGE";
|
|
47
|
+
export const VIDEO = "VIDEO";
|
|
48
|
+
export const DOCUMENT = "DOCUMENT";
|
|
49
|
+
export const CTA = "CTA";
|
|
50
|
+
export const QUICK_REPLY = "QUICK_REPLY";
|
|
51
|
+
export const BEE = "BEE";
|
|
46
52
|
export const IOS = "IOS";
|
|
47
53
|
export const ANDROID = "ANDROID";
|
|
48
54
|
export const EXTERNAL_URL = "EXTERNAL_URL";
|