@capillarytech/creatives-library 9.0.54 → 9.0.55-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/constants/unified.js +0 -1
- package/package.json +1 -1
- package/services/api.js +20 -0
- package/utils/common.js +0 -4
- package/v2Components/CommonTestAndPreview/index.js +1 -1
- package/v2Containers/CommunicationFlow/CommunicationFlow.js +118 -56
- package/v2Containers/CommunicationFlow/CommunicationFlow.scss +1 -1
- package/v2Containers/CommunicationFlow/CommunicationFlowCard.js +22 -6
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +421 -49
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlowCard.test.js +18 -2
- package/v2Containers/CommunicationFlow/constants.js +90 -1
- package/v2Containers/CommunicationFlow/index.js +15 -20
- package/v2Containers/CommunicationFlow/messages.js +4 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.js +6 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +25 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/deliverySettingsConfig.test.js +11 -4
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/deliverySettingsConfig.js +24 -5
- package/v2Containers/CreativesContainer/index.js +1 -1
|
@@ -38,6 +38,95 @@ export const CHANNEL_PRIORITY = 'CHANNEL_PRIORITY';
|
|
|
38
38
|
export const AB_TEST = 'AB_TEST';
|
|
39
39
|
export const SINGLE_TEMPLATE = 'SINGLE_TEMPLATE';
|
|
40
40
|
|
|
41
|
+
// CCS CommDefinition strategyType — distinct from SINGLE_TEMPLATE above, which
|
|
42
|
+
// is this UI's own communicationStrategy value. Only SINGLE is supported this
|
|
43
|
+
// phase; CCS create is skipped entirely for CHANNEL_PRIORITY/AB_TEST.
|
|
44
|
+
export const CCS_STRATEGY_TYPE_SINGLE = 'SINGLE';
|
|
45
|
+
|
|
46
|
+
// Mirrors CCS's SingleChannelStrategy field names exactly (Cap Notify API
|
|
47
|
+
// Design doc sample payloads, verified against real create responses). Kept
|
|
48
|
+
// separate from CHANNEL_CONTENT_KEY_MAP/CHANNEL_DELIVERY_KEY_MAP below, which
|
|
49
|
+
// are the legacy messageMeta payload's keys.
|
|
50
|
+
export const CCS_CHANNEL_CONTENT_KEY_MAP = {
|
|
51
|
+
SMS: 'smsMessageContent',
|
|
52
|
+
EMAIL: 'emailMessageContent',
|
|
53
|
+
WHATSAPP: 'whatsappMessageContent',
|
|
54
|
+
// CCS's channel enum value for mobile push is 'PUSH' (verified against real
|
|
55
|
+
// create responses), even though the content/delivery keys stay 'mpush*'.
|
|
56
|
+
PUSH: 'mpushMessageContent',
|
|
57
|
+
INAPP: 'inAppMessageContent',
|
|
58
|
+
ANDROID: 'androidMessageContent',
|
|
59
|
+
IOS: 'iosMessageContent',
|
|
60
|
+
ZALO: 'zaloMessageContent',
|
|
61
|
+
VIBER: 'viberMessageContent',
|
|
62
|
+
LINE: 'lineMessageContent',
|
|
63
|
+
WEBPUSH: 'webPushMessageContent',
|
|
64
|
+
RCS: 'rcsMessageContent',
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// contentItem.templateData is built by the legacy CreativesContainer content
|
|
68
|
+
// editor (messageMeta-oriented field names) — for channels whose legacy field
|
|
69
|
+
// names don't match CCS's own content schema, transform before sending rather
|
|
70
|
+
// than dumping templateData as-is. EMAIL: legacy stores the subject as
|
|
71
|
+
// `emailSubject` and the HTML body as `emailBody` (see
|
|
72
|
+
// extractContentForPreview.js and CreativesContainer/index.js, where the Bee
|
|
73
|
+
// editor's output is written back into templateData.emailBody — also
|
|
74
|
+
// mirrored by the existing legacy emailMessageContent builder in
|
|
75
|
+
// utils/transformerUtils.js). CCS expects `messageSubject`/`messageBody`.
|
|
76
|
+
export const CCS_CONTENT_TRANSFORMS = {
|
|
77
|
+
EMAIL: (templateData = {}) => ({
|
|
78
|
+
messageSubject: templateData.emailSubject,
|
|
79
|
+
messageBody: templateData.emailBody || templateData.emailHtml,
|
|
80
|
+
}),
|
|
81
|
+
// SMS: legacy stores the typed text as `messageBody` (see
|
|
82
|
+
// CreativesContainer/index.js's getCreativesData SMS case, and the existing
|
|
83
|
+
// legacy smsMessageContent builder in utils/transformerUtils.js, both of
|
|
84
|
+
// which read/write this same field) — CCS's schema expects it as `message`
|
|
85
|
+
// (confirmed against a real CCS response showing smsMessageContent.message
|
|
86
|
+
// stayed null when messageBody was sent instead).
|
|
87
|
+
SMS: (templateData = {}) => ({
|
|
88
|
+
message: templateData.messageBody || '',
|
|
89
|
+
}),
|
|
90
|
+
// WEBPUSH: the legacy editor nests the real data under
|
|
91
|
+
// messageContent.content (see CommunicationFlowCard.js's own comment on
|
|
92
|
+
// this same wrapping, for the edit-mode case) — extracting it flat here
|
|
93
|
+
// matches CCS's shape ({ messageSubject, accountId, content }); `offers`
|
|
94
|
+
// (a legacy-only field) is deliberately dropped, not part of CCS's schema.
|
|
95
|
+
WEBPUSH: (templateData = {}) => {
|
|
96
|
+
const inner = templateData.messageContent?.content || templateData;
|
|
97
|
+
return {
|
|
98
|
+
messageSubject: inner.messageSubject,
|
|
99
|
+
accountId: inner.accountId,
|
|
100
|
+
content: inner.content,
|
|
101
|
+
};
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const CCS_CHANNEL_DELIVERY_KEY_MAP = {
|
|
106
|
+
SMS: 'smsDeliverySettings',
|
|
107
|
+
EMAIL: 'emailDeliverySettings',
|
|
108
|
+
WHATSAPP: 'whatsappDeliverySettings',
|
|
109
|
+
PUSH: 'mpushDeliverySettings',
|
|
110
|
+
INAPP: 'inAppDeliverySettings',
|
|
111
|
+
ANDROID: 'androidDeliverySettings',
|
|
112
|
+
IOS: 'iosDeliverySettings',
|
|
113
|
+
ZALO: 'zaloDeliverySettings',
|
|
114
|
+
VIBER: 'viberDeliverySettings',
|
|
115
|
+
LINE: 'lineDeliverySettings',
|
|
116
|
+
WEBPUSH: 'webPushDeliverySettings',
|
|
117
|
+
RCS: 'rcsDeliverySettings',
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// The UI's own internal channel identifier for mobile push is 'MOBILEPUSH'
|
|
121
|
+
// (see CreativesContainer/constants.js MOBILE_PUSH), but CCS's channel enum
|
|
122
|
+
// value is 'PUSH' (verified against real create responses — the content/
|
|
123
|
+
// delivery KEYS still say 'mpush*', only the enum VALUE is 'PUSH') —
|
|
124
|
+
// normalize before building the CCS payload. Every other channel identifier
|
|
125
|
+
// matches CCS as-is.
|
|
126
|
+
export const CCS_CHANNEL_NAME_MAP = {
|
|
127
|
+
MOBILEPUSH: 'PUSH',
|
|
128
|
+
};
|
|
129
|
+
|
|
41
130
|
// Channel config - single source of truth for CreativesContainer and TemplatesV2
|
|
42
131
|
// paneKey: TemplatesV2 defaultPanes object key (for channelsToHide)
|
|
43
132
|
// channelProp: CreativesContainer channel prop (must match pane.key for tab to be active)
|
|
@@ -232,4 +321,4 @@ export const INCENTIVE_TYPES = [
|
|
|
232
321
|
{
|
|
233
322
|
value: 'badges', label: <FormattedMessage {...messages.badges} />, isActive: false, disabled: false,
|
|
234
323
|
},
|
|
235
|
-
];
|
|
324
|
+
];
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
import React from 'react';
|
|
10
10
|
import PropTypes from 'prop-types';
|
|
11
11
|
import CommunicationFlow from './CommunicationFlow';
|
|
12
|
-
import { hasSupportEngagementModule } from '../../utils/common';
|
|
13
12
|
|
|
14
13
|
const CommunicationFlowContainer = ({
|
|
15
14
|
config,
|
|
@@ -18,22 +17,16 @@ const CommunicationFlowContainer = ({
|
|
|
18
17
|
onCancel,
|
|
19
18
|
onChange,
|
|
20
19
|
...otherProps
|
|
21
|
-
}) =>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
onCancel={onCancel}
|
|
32
|
-
onChange={onChange}
|
|
33
|
-
{...otherProps}
|
|
34
|
-
/>
|
|
35
|
-
);
|
|
36
|
-
};
|
|
20
|
+
}) => (
|
|
21
|
+
<CommunicationFlow
|
|
22
|
+
config={config}
|
|
23
|
+
initialData={initialData}
|
|
24
|
+
onSave={onSave}
|
|
25
|
+
onCancel={onCancel}
|
|
26
|
+
onChange={onChange}
|
|
27
|
+
{...otherProps}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
37
30
|
|
|
38
31
|
CommunicationFlowContainer.propTypes = {
|
|
39
32
|
config: PropTypes.shape({
|
|
@@ -87,11 +80,13 @@ CommunicationFlowContainer.propTypes = {
|
|
|
87
80
|
controls: PropTypes.array,
|
|
88
81
|
}),
|
|
89
82
|
}),
|
|
90
|
-
context: PropTypes.object, // ouId, campaignId, programId,
|
|
91
|
-
|
|
83
|
+
context: PropTypes.object, // ouId, campaignId, programId; name (required for CCS create),
|
|
84
|
+
// referenceId (optional, auto-generated from name if absent), description (optional).
|
|
85
|
+
useCCS: PropTypes.bool, // If false, skips the CCS createCommDefinition call on save. Defaults to true.
|
|
92
86
|
}).isRequired,
|
|
93
87
|
initialData: PropTypes.object, // for edit/preview mode
|
|
94
|
-
onSave: PropTypes.func.isRequired, // (data) => void - called when user saves
|
|
88
|
+
onSave: PropTypes.func.isRequired, // (data) => void - called when user saves; data.ccsCommDefinition
|
|
89
|
+
// ({id, referenceId, version, status}) is present when the CCS create succeeded
|
|
95
90
|
onCancel: PropTypes.func.isRequired, // () => void - called when user cancels
|
|
96
91
|
onChange: PropTypes.func, // (data) => void - optional, called on data changes
|
|
97
92
|
};
|
|
@@ -363,4 +363,8 @@ export default {
|
|
|
363
363
|
id: `${prefix}.senderNotConfiguredError`,
|
|
364
364
|
defaultMessage: 'Selected domain gateway id is not correct. Please change the domain id or contact the gateway team to register them with Capillary.',
|
|
365
365
|
},
|
|
366
|
+
duplicateReferenceIdError: {
|
|
367
|
+
id: `${prefix}.duplicateReferenceIdError`,
|
|
368
|
+
defaultMessage: 'This reference ID is already in use in your organization. Please use a different reference ID.',
|
|
369
|
+
},
|
|
366
370
|
};
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
getEmailDefaultsForDomainId,
|
|
30
30
|
getSmsDefaultsForDomainId,
|
|
31
31
|
getRcsAccountName,
|
|
32
|
+
getEmailDomainGatewayMapIdByDomainId,
|
|
32
33
|
} from './deliverySettingsConfig';
|
|
33
34
|
import messages from '../../messages';
|
|
34
35
|
import './SenderDetails.scss';
|
|
@@ -151,6 +152,11 @@ const SenderDetails = ({
|
|
|
151
152
|
saveValues.whatsappDomainId = matchedDomain?.domainProperties?.id ?? matchedDomain?.domainId ?? matchedDomain?.id;
|
|
152
153
|
}
|
|
153
154
|
}
|
|
155
|
+
// Include EMAIL domainGatewayMapId derived from the selected domain for persistence -
|
|
156
|
+
// CCS needs it alongside domainId, but it's not a user-facing field.
|
|
157
|
+
if (entity && fieldValues.emailDomain != null && fieldValues.emailDomain !== '') {
|
|
158
|
+
saveValues.emailDomainGatewayMapId = getEmailDomainGatewayMapIdByDomainId(entity, fieldValues.emailDomain);
|
|
159
|
+
}
|
|
154
160
|
onSave(saveValues);
|
|
155
161
|
}
|
|
156
162
|
onClose();
|
package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js
CHANGED
|
@@ -83,6 +83,7 @@ const ENTITIES = {
|
|
|
83
83
|
emailTwoDomains: {
|
|
84
84
|
EMAIL: [
|
|
85
85
|
{
|
|
86
|
+
id: 'gwmap-a',
|
|
86
87
|
domainProperties: {
|
|
87
88
|
id: 'em-a',
|
|
88
89
|
domainName: 'Mail Alpha',
|
|
@@ -101,6 +102,7 @@ const ENTITIES = {
|
|
|
101
102
|
},
|
|
102
103
|
},
|
|
103
104
|
{
|
|
105
|
+
id: 'gwmap-b',
|
|
104
106
|
domainProperties: {
|
|
105
107
|
id: 'em-b',
|
|
106
108
|
domainName: 'Mail Beta',
|
|
@@ -322,6 +324,29 @@ describe('SenderDetails', () => {
|
|
|
322
324
|
});
|
|
323
325
|
});
|
|
324
326
|
|
|
327
|
+
it('persists emailDomainGatewayMapId derived from the selected email domain on save', async () => {
|
|
328
|
+
const { onSave } = renderSenderDetails({
|
|
329
|
+
channels: ['EMAIL'],
|
|
330
|
+
preloadedDomainProperties: ENTITIES.emailTwoDomains,
|
|
331
|
+
});
|
|
332
|
+
const root = document.querySelector('.sender-details');
|
|
333
|
+
await waitFor(() => expect(within(root).getByText('Mail Alpha')).toBeInTheDocument());
|
|
334
|
+
|
|
335
|
+
await openSelectAndChoose(root, 0, 'Mail Beta');
|
|
336
|
+
await waitFor(() => expect(within(root).getByText('beta@other.com')).toBeInTheDocument());
|
|
337
|
+
|
|
338
|
+
const saveBtn = within(root).getByRole('button', { name: /save changes/i });
|
|
339
|
+
await waitFor(() => expect(saveBtn).not.toBeDisabled());
|
|
340
|
+
fireEvent.click(saveBtn);
|
|
341
|
+
|
|
342
|
+
expect(onSave).toHaveBeenCalledWith(
|
|
343
|
+
expect.objectContaining({
|
|
344
|
+
emailDomain: 'em-b',
|
|
345
|
+
emailDomainGatewayMapId: 'gwmap-b',
|
|
346
|
+
}),
|
|
347
|
+
);
|
|
348
|
+
});
|
|
349
|
+
|
|
325
350
|
it('resets email domain row back to the first gateway', async () => {
|
|
326
351
|
renderSenderDetails({
|
|
327
352
|
channels: ['EMAIL'],
|
|
@@ -438,6 +438,7 @@ describe('deliverySettingsConfig — user-facing flows', () => {
|
|
|
438
438
|
smsDomain: 'd1',
|
|
439
439
|
smsSenderId: 'g1',
|
|
440
440
|
emailDomain: 'ed',
|
|
441
|
+
emailDomainGatewayMapId: 'gwmap-1',
|
|
441
442
|
emailSenderId: 'mail@x.com',
|
|
442
443
|
emailSenderName: 'N',
|
|
443
444
|
emailReplyToId: 'r@x.com',
|
|
@@ -455,9 +456,10 @@ describe('deliverySettingsConfig — user-facing flows', () => {
|
|
|
455
456
|
SMS: { domainId: 'd1', gsmSenderId: 'g1' },
|
|
456
457
|
EMAIL: {
|
|
457
458
|
domainId: 'ed',
|
|
458
|
-
|
|
459
|
+
domainGatewayMapId: 'gwmap-1',
|
|
460
|
+
senderId: 'mail@x.com',
|
|
459
461
|
senderLabel: 'N',
|
|
460
|
-
|
|
462
|
+
replyToId: 'r@x.com',
|
|
461
463
|
},
|
|
462
464
|
WHATSAPP: { domainId: 'wd', senderMobNum: '+w' },
|
|
463
465
|
RCS: { senderMobNum: '+r', rcsSender: '+r' },
|
|
@@ -472,9 +474,10 @@ describe('deliverySettingsConfig — user-facing flows', () => {
|
|
|
472
474
|
SMS: { domainId: 'd', gsmSenderId: 'g' },
|
|
473
475
|
EMAIL: {
|
|
474
476
|
domainId: 'ed',
|
|
475
|
-
|
|
477
|
+
domainGatewayMapId: 'gwmap-2',
|
|
478
|
+
senderId: 'e@e.com',
|
|
476
479
|
senderLabel: 'L',
|
|
477
|
-
|
|
480
|
+
replyToId: 'r@e.com',
|
|
478
481
|
},
|
|
479
482
|
WHATSAPP: { senderMobNum: '+w', domainId: 'wid' },
|
|
480
483
|
RCS: { rcsSender: '+r' },
|
|
@@ -486,6 +489,10 @@ describe('deliverySettingsConfig — user-facing flows', () => {
|
|
|
486
489
|
smsDomain: 'd',
|
|
487
490
|
smsSenderId: 'g',
|
|
488
491
|
emailDomain: 'ed',
|
|
492
|
+
emailDomainGatewayMapId: 'gwmap-2',
|
|
493
|
+
emailSenderId: 'e@e.com',
|
|
494
|
+
emailSenderName: 'L',
|
|
495
|
+
emailReplyToId: 'r@e.com',
|
|
489
496
|
whatsappDomainId: 'wid',
|
|
490
497
|
viberSenderId: 'vx',
|
|
491
498
|
});
|
|
@@ -181,6 +181,14 @@ const getEmailDomainValue = (entity) => {
|
|
|
181
181
|
return emailData.domainProperties?.id ?? emailData.domainId ?? emailData.id ?? '';
|
|
182
182
|
};
|
|
183
183
|
|
|
184
|
+
// CCS's domainGatewayMapId is the domainProperties API's own outer `id`
|
|
185
|
+
// (distinct from `domainProperties.id`, which is the numeric domainId) - the
|
|
186
|
+
// domain-gateway-mapping id for the selected domain entry.
|
|
187
|
+
const getEmailDomainGatewayMapIdByDomainId = (entity, domainId) => {
|
|
188
|
+
const domainEntry = getEmailDomainById(entity, domainId);
|
|
189
|
+
return domainEntry?.id ?? '';
|
|
190
|
+
};
|
|
191
|
+
|
|
184
192
|
/** Domain name for summary display (parseEntityForDisplay) */
|
|
185
193
|
const getEmailDomainNameForDisplay = (entity) => {
|
|
186
194
|
const emailData = getEmailData(entity);
|
|
@@ -262,6 +270,8 @@ const getEmailDefaultsForDomain = (domainData) => {
|
|
|
262
270
|
* context.fieldValues has current form state (e.g. selected emailDomain)
|
|
263
271
|
* Aligned with adiona-ui/cap-campaigns-v2: default = contactInfo item with default:true, or first
|
|
264
272
|
*/
|
|
273
|
+
export { getEmailDomainGatewayMapIdByDomainId };
|
|
274
|
+
|
|
265
275
|
export const getDefaultValueForField = (fieldKey, entity, context = {}) => {
|
|
266
276
|
if (!entity) return '';
|
|
267
277
|
const fieldValues = context?.fieldValues || {};
|
|
@@ -574,7 +584,11 @@ export const parseEntityForDisplay = (entity, context = {}) => {
|
|
|
574
584
|
senderDetails: entity,
|
|
575
585
|
smsSenderId: getSmsSenderIdValue(entity),
|
|
576
586
|
smsDomain: getSmsDomainValue(entity),
|
|
587
|
+
// emailDomain here is the domain NAME (for display only) - buildChannelSettingFromFieldValues
|
|
588
|
+
// needs the numeric domainId/domainGatewayMapId, sourced separately below.
|
|
577
589
|
emailDomain: getEmailDomainNameForDisplay(entity),
|
|
590
|
+
emailDomainId: getEmailDomainValue(entity),
|
|
591
|
+
emailDomainGatewayMapId: getEmailDomainGatewayMapIdByDomainId(entity, getEmailDomainValue(entity)),
|
|
578
592
|
emailSenderId: getEmailSenderIdValue(entity),
|
|
579
593
|
viberSenderId: getSenderIdFromContactInfo(entity, 'VIBER'),
|
|
580
594
|
zaloSenderId: getSenderIdFromContactInfo(entity, 'ZALO'),
|
|
@@ -600,10 +614,14 @@ export const buildChannelSettingFromFieldValues = (fieldValues = {}) => {
|
|
|
600
614
|
}
|
|
601
615
|
if (fieldValues.emailDomain != null || fieldValues.emailSenderId != null || fieldValues.emailSenderName != null || fieldValues.emailReplyToId != null) {
|
|
602
616
|
channelSetting.EMAIL = {
|
|
603
|
-
|
|
604
|
-
|
|
617
|
+
// emailDomainId (numeric, from the auto-save-defaults path via parseEntityForDisplay)
|
|
618
|
+
// takes priority over emailDomain, which is numeric when sourced from SenderDetails'
|
|
619
|
+
// own field state but a display NAME string when sourced from parseEntityForDisplay.
|
|
620
|
+
domainId: fieldValues.emailDomainId ?? fieldValues.emailDomain,
|
|
621
|
+
domainGatewayMapId: fieldValues.emailDomainGatewayMapId,
|
|
622
|
+
senderId: fieldValues.emailSenderId,
|
|
605
623
|
senderLabel: fieldValues.emailSenderName,
|
|
606
|
-
|
|
624
|
+
replyToId: fieldValues.emailReplyToId,
|
|
607
625
|
};
|
|
608
626
|
}
|
|
609
627
|
if (fieldValues.whatsappSenderNumber != null || fieldValues.whatsappAccount != null) {
|
|
@@ -673,9 +691,10 @@ export const parseChannelSettingForDisplay = (channelSetting = {}) => {
|
|
|
673
691
|
}
|
|
674
692
|
if (channelSetting.EMAIL) {
|
|
675
693
|
out.emailDomain = channelSetting.EMAIL.domainId;
|
|
676
|
-
out.
|
|
694
|
+
out.emailDomainGatewayMapId = channelSetting.EMAIL.domainGatewayMapId;
|
|
695
|
+
out.emailSenderId = channelSetting.EMAIL.senderId;
|
|
677
696
|
out.emailSenderName = channelSetting.EMAIL.senderLabel;
|
|
678
|
-
out.emailReplyToId = channelSetting.EMAIL.
|
|
697
|
+
out.emailReplyToId = channelSetting.EMAIL.replyToId;
|
|
679
698
|
}
|
|
680
699
|
if (channelSetting.WHATSAPP) {
|
|
681
700
|
out.whatsappSenderNumber = channelSetting.WHATSAPP.senderMobNum;
|