@capillarytech/creatives-library 9.0.55 → 9.0.56-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 +12 -0
- package/utils/common.js +0 -4
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +13 -0
- package/v2Components/CommonTestAndPreview/constants.js +2 -0
- package/v2Components/CommonTestAndPreview/index.js +1 -1
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +106 -0
- package/v2Components/CommonTestAndPreview/tests/constants.test.js +9 -1
- package/v2Containers/CommunicationFlow/CommunicationFlow.js +109 -56
- package/v2Containers/CommunicationFlow/CommunicationFlow.scss +1 -1
- package/v2Containers/CommunicationFlow/CommunicationFlowCard.js +60 -27
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +441 -51
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlowCard.test.js +40 -4
- package/v2Containers/CommunicationFlow/Tests/constants.test.js +85 -0
- package/v2Containers/CommunicationFlow/constants.js +63 -4
- package/v2Containers/CommunicationFlow/index.js +15 -20
- package/v2Containers/CommunicationFlow/messages.js +7 -3
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.js +0 -4
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +2 -2
- 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 +19 -5
- package/v2Containers/CreativesContainer/index.js +1 -1
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +5 -5
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -12
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +3 -3
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -34
- package/.vscode/settings.json +0 -3
|
@@ -108,6 +108,22 @@ describe('CommunicationFlowCard', () => {
|
|
|
108
108
|
fireEvent.click(screen.getByText('Add creatives'));
|
|
109
109
|
expect(screen.getByTestId('comm-flow-mock')).toHaveAttribute('data-mode', 'create');
|
|
110
110
|
});
|
|
111
|
+
|
|
112
|
+
it('disables the Add creatives button when disabled prop is true', () => {
|
|
113
|
+
renderCard({ disabled: true, disabledTooltip: 'Enter an Alert name to add content' });
|
|
114
|
+
expect(screen.getByText('Add creatives').closest('button')).toBeDisabled();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('does not open SlideBox when Add creatives is clicked while disabled', () => {
|
|
118
|
+
renderCard({ disabled: true, disabledTooltip: 'Enter an Alert name to add content' });
|
|
119
|
+
fireEvent.click(screen.getByText('Add creatives'));
|
|
120
|
+
expect(screen.queryByTestId('slide-box')).not.toBeInTheDocument();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('Add creatives button is enabled by default (disabled prop absent)', () => {
|
|
124
|
+
renderCard();
|
|
125
|
+
expect(screen.getByText('Add creatives').closest('button')).not.toBeDisabled();
|
|
126
|
+
});
|
|
111
127
|
});
|
|
112
128
|
|
|
113
129
|
describe('configured state (initialData provided)', () => {
|
|
@@ -186,6 +202,26 @@ describe('CommunicationFlowCard', () => {
|
|
|
186
202
|
renderCard({ initialData: makeSavedData() });
|
|
187
203
|
expect(screen.getByText('Other controls')).toBeInTheDocument();
|
|
188
204
|
});
|
|
205
|
+
|
|
206
|
+
it('hides the Other controls column when config.features.showDynamicControls is false', () => {
|
|
207
|
+
renderCard({
|
|
208
|
+
config: { ...baseConfig, features: { showDynamicControls: false } },
|
|
209
|
+
initialData: makeSavedData({
|
|
210
|
+
dynamicControls: {
|
|
211
|
+
sendToControlCustomers: false, sendToBrandPocs: false, useTinyUrl: false, overrideDailyLimit: false,
|
|
212
|
+
},
|
|
213
|
+
}),
|
|
214
|
+
});
|
|
215
|
+
expect(screen.queryByText('Other controls')).not.toBeInTheDocument();
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('still shows the Other controls column when showDynamicControls is unset (Engagement Module default)', () => {
|
|
219
|
+
renderCard({
|
|
220
|
+
config: { ...baseConfig, features: {} },
|
|
221
|
+
initialData: makeSavedData(),
|
|
222
|
+
});
|
|
223
|
+
expect(screen.getByText('Other controls')).toBeInTheDocument();
|
|
224
|
+
});
|
|
189
225
|
});
|
|
190
226
|
|
|
191
227
|
describe('channel config resolution', () => {
|
|
@@ -231,11 +267,11 @@ describe('CommunicationFlowCard', () => {
|
|
|
231
267
|
expect(screen.getByText(/Sender ID/)).toBeInTheDocument();
|
|
232
268
|
});
|
|
233
269
|
|
|
234
|
-
it('shows
|
|
270
|
+
it('shows senderId for EMAIL with Sender ID label', () => {
|
|
235
271
|
renderCard({
|
|
236
272
|
initialData: makeSavedData({
|
|
237
273
|
contentItems: [{ channel: 'EMAIL', templateData: {} }],
|
|
238
|
-
deliverySetting: { channelSetting: { EMAIL: {
|
|
274
|
+
deliverySetting: { channelSetting: { EMAIL: { senderId: 'test@example.com' } } },
|
|
239
275
|
}),
|
|
240
276
|
});
|
|
241
277
|
expect(screen.getByText(/test@example.com/)).toBeInTheDocument();
|
|
@@ -545,10 +581,10 @@ describe('CommunicationFlowCard', () => {
|
|
|
545
581
|
});
|
|
546
582
|
|
|
547
583
|
describe('SlideBox header', () => {
|
|
548
|
-
it('renders Add
|
|
584
|
+
it('renders Add content title in the slidebox header', () => {
|
|
549
585
|
renderCard();
|
|
550
586
|
fireEvent.click(screen.getByText('Add creatives'));
|
|
551
|
-
expect(screen.getByText('Add
|
|
587
|
+
expect(screen.getByText('Add content')).toBeInTheDocument();
|
|
552
588
|
});
|
|
553
589
|
});
|
|
554
590
|
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CCS_CONTENT_TRANSFORMS,
|
|
3
|
+
CCS_CHANNEL_NAME_MAP,
|
|
4
|
+
CCS_CHANNEL_CONTENT_KEY_MAP,
|
|
5
|
+
CCS_CHANNEL_DELIVERY_KEY_MAP,
|
|
6
|
+
} from '../constants';
|
|
7
|
+
|
|
8
|
+
describe('CCS_CONTENT_TRANSFORMS', () => {
|
|
9
|
+
describe('EMAIL', () => {
|
|
10
|
+
it('prefers emailBody over emailHtml when both are present', () => {
|
|
11
|
+
expect(
|
|
12
|
+
CCS_CONTENT_TRANSFORMS.EMAIL({
|
|
13
|
+
emailSubject: 'Subject', emailBody: '<p>Body</p>', emailHtml: '<p>Html</p>',
|
|
14
|
+
}),
|
|
15
|
+
).toEqual({ messageSubject: 'Subject', messageBody: '<p>Body</p>' });
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('falls back to emailHtml when emailBody is absent', () => {
|
|
19
|
+
expect(
|
|
20
|
+
CCS_CONTENT_TRANSFORMS.EMAIL({ emailSubject: 'Subject', emailHtml: '<p>Html</p>' }),
|
|
21
|
+
).toEqual({ messageSubject: 'Subject', messageBody: '<p>Html</p>' });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('defaults templateData to {} when called with no argument', () => {
|
|
25
|
+
expect(CCS_CONTENT_TRANSFORMS.EMAIL()).toEqual({
|
|
26
|
+
messageSubject: undefined, messageBody: undefined,
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('SMS', () => {
|
|
32
|
+
it('uses messageBody when present', () => {
|
|
33
|
+
expect(CCS_CONTENT_TRANSFORMS.SMS({ messageBody: 'Hello' })).toEqual({ message: 'Hello' });
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('falls back to an empty string when messageBody is absent', () => {
|
|
37
|
+
expect(CCS_CONTENT_TRANSFORMS.SMS({})).toEqual({ message: '' });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('defaults templateData to {} when called with no argument', () => {
|
|
41
|
+
expect(CCS_CONTENT_TRANSFORMS.SMS()).toEqual({ message: '' });
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('WEBPUSH', () => {
|
|
46
|
+
it('extracts fields from the nested messageContent.content wrapper', () => {
|
|
47
|
+
expect(
|
|
48
|
+
CCS_CONTENT_TRANSFORMS.WEBPUSH({
|
|
49
|
+
messageContent: {
|
|
50
|
+
content: {
|
|
51
|
+
messageSubject: 'Subject', accountId: 'acc_1', content: 'Body', offers: [{ id: 1 }],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
}),
|
|
55
|
+
).toEqual({ messageSubject: 'Subject', accountId: 'acc_1', content: 'Body' });
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('falls back to the flat templateData when messageContent.content is absent', () => {
|
|
59
|
+
expect(
|
|
60
|
+
CCS_CONTENT_TRANSFORMS.WEBPUSH({
|
|
61
|
+
messageSubject: 'Subject', accountId: 'acc_1', content: 'Body',
|
|
62
|
+
}),
|
|
63
|
+
).toEqual({ messageSubject: 'Subject', accountId: 'acc_1', content: 'Body' });
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('defaults templateData to {} when called with no argument', () => {
|
|
67
|
+
expect(CCS_CONTENT_TRANSFORMS.WEBPUSH()).toEqual({
|
|
68
|
+
messageSubject: undefined, accountId: undefined, content: undefined,
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('CCS_CHANNEL_NAME_MAP', () => {
|
|
75
|
+
it('normalizes the UI\'s MOBILEPUSH identifier to CCS\'s PUSH enum value', () => {
|
|
76
|
+
expect(CCS_CHANNEL_NAME_MAP.MOBILEPUSH).toBe('PUSH');
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('CCS_CHANNEL_CONTENT_KEY_MAP / CCS_CHANNEL_DELIVERY_KEY_MAP', () => {
|
|
81
|
+
it('maps the PUSH channel to its mpush-prefixed content and delivery keys', () => {
|
|
82
|
+
expect(CCS_CHANNEL_CONTENT_KEY_MAP.PUSH).toBe('mpushMessageContent');
|
|
83
|
+
expect(CCS_CHANNEL_DELIVERY_KEY_MAP.PUSH).toBe('mpushDeliverySettings');
|
|
84
|
+
});
|
|
85
|
+
});
|
|
@@ -38,9 +38,68 @@ 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
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
// CCS CommDefinition strategyType — distinct from SINGLE_TEMPLATE above, which is this UI's own communicationStrategy value. Only SINGLE is supported this phase; CCS create is skipped entirely for CHANNEL_PRIORITY/AB_TEST.
|
|
42
|
+
export const CCS_STRATEGY_TYPE_SINGLE = 'SINGLE';
|
|
43
|
+
|
|
44
|
+
// Mirrors CCS SingleChannelStrategy field names exactly (per Cap Notify API design samples and real create responses); kept separate from the legacy messageMeta CHANNEL_CONTENT_KEY_MAP/CHANNEL_DELIVERY_KEY_MAP.
|
|
45
|
+
export const CCS_CHANNEL_CONTENT_KEY_MAP = {
|
|
46
|
+
SMS: 'smsMessageContent',
|
|
47
|
+
EMAIL: 'emailMessageContent',
|
|
48
|
+
WHATSAPP: 'whatsappMessageContent',
|
|
49
|
+
// CCS's channel enum value for mobile push is 'PUSH' (verified against real create responses), even though the content/delivery keys stay 'mpush*'.
|
|
50
|
+
PUSH: 'mpushMessageContent',
|
|
51
|
+
INAPP: 'inAppMessageContent',
|
|
52
|
+
ANDROID: 'androidMessageContent',
|
|
53
|
+
IOS: 'iosMessageContent',
|
|
54
|
+
ZALO: 'zaloMessageContent',
|
|
55
|
+
VIBER: 'viberMessageContent',
|
|
56
|
+
LINE: 'lineMessageContent',
|
|
57
|
+
WEBPUSH: 'webPushMessageContent',
|
|
58
|
+
RCS: 'rcsMessageContent',
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// contentItem.templateData uses legacy messageMeta field names, so transform mismatched channel fields before sending to CCS; for EMAIL, map legacy `emailSubject`/`emailBody` to CCS's `messageSubject`/`messageBody`.
|
|
62
|
+
export const CCS_CONTENT_TRANSFORMS = {
|
|
63
|
+
EMAIL: (templateData = {}) => ({
|
|
64
|
+
messageSubject: templateData.emailSubject,
|
|
65
|
+
messageBody: templateData.emailBody || templateData.emailHtml,
|
|
66
|
+
}),
|
|
67
|
+
// SMS: legacy stores text as `messageBody`, while CCS expects `message`; map the field explicitly to avoid CCS returning smsMessageContent.message as null.
|
|
68
|
+
SMS: (templateData = {}) => ({
|
|
69
|
+
message: templateData.messageBody || '',
|
|
70
|
+
}),
|
|
71
|
+
// WEBPUSH: the legacy editor nests data under messageContent.content; flatten it to CCS's { messageSubject, accountId, content } shape and drop the legacy-only `offers` field.
|
|
72
|
+
WEBPUSH: (templateData = {}) => {
|
|
73
|
+
const inner = templateData.messageContent?.content || templateData;
|
|
74
|
+
return {
|
|
75
|
+
messageSubject: inner.messageSubject,
|
|
76
|
+
accountId: inner.accountId,
|
|
77
|
+
content: inner.content,
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const CCS_CHANNEL_DELIVERY_KEY_MAP = {
|
|
83
|
+
SMS: 'smsDeliverySettings',
|
|
84
|
+
EMAIL: 'emailDeliverySettings',
|
|
85
|
+
WHATSAPP: 'whatsappDeliverySettings',
|
|
86
|
+
PUSH: 'mpushDeliverySettings',
|
|
87
|
+
INAPP: 'inAppDeliverySettings',
|
|
88
|
+
ANDROID: 'androidDeliverySettings',
|
|
89
|
+
IOS: 'iosDeliverySettings',
|
|
90
|
+
ZALO: 'zaloDeliverySettings',
|
|
91
|
+
VIBER: 'viberDeliverySettings',
|
|
92
|
+
LINE: 'lineDeliverySettings',
|
|
93
|
+
WEBPUSH: 'webPushDeliverySettings',
|
|
94
|
+
RCS: 'rcsDeliverySettings',
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// The UI uses `MOBILEPUSH`, while CCS uses `PUSH` for the channel enum (verified against real create responses); normalize this value before building the CCS payload, while retaining the `mpush*` content/delivery keys. All other channel identifiers pass through unchanged.
|
|
98
|
+
export const CCS_CHANNEL_NAME_MAP = {
|
|
99
|
+
MOBILEPUSH: 'PUSH',
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Channel config — single source of truth for CreativesContainer and TemplatesV2; paneKey is the TemplatesV2 defaultPanes key used by channelsToHide, and channelProp is the CreativesContainer channel prop that must match pane.key for the tab to be active.
|
|
44
103
|
export const CHANNELS = [
|
|
45
104
|
{
|
|
46
105
|
value: 'SMS',
|
|
@@ -232,4 +291,4 @@ export const INCENTIVE_TYPES = [
|
|
|
232
291
|
{
|
|
233
292
|
value: 'badges', label: <FormattedMessage {...messages.badges} />, isActive: false, disabled: false,
|
|
234
293
|
},
|
|
235
|
-
];
|
|
294
|
+
];
|
|
@@ -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
|
};
|
|
@@ -64,9 +64,9 @@ export default {
|
|
|
64
64
|
},
|
|
65
65
|
|
|
66
66
|
// Slidebox header
|
|
67
|
-
|
|
68
|
-
id: `${prefix}.
|
|
69
|
-
defaultMessage: 'Add
|
|
67
|
+
addContent: {
|
|
68
|
+
id: `${prefix}.addContent`,
|
|
69
|
+
defaultMessage: 'Add content',
|
|
70
70
|
},
|
|
71
71
|
|
|
72
72
|
// Step prompts
|
|
@@ -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
|
};
|
|
@@ -407,10 +407,6 @@ const ChannelSelectionStep = ({
|
|
|
407
407
|
return (
|
|
408
408
|
<>
|
|
409
409
|
<CapRow className="channel-selection-step">
|
|
410
|
-
<CapHeading type="h3" className="margin-b-8">
|
|
411
|
-
{formatMessage(messages.contentTemplate)}
|
|
412
|
-
</CapHeading>
|
|
413
|
-
|
|
414
410
|
<CapRow
|
|
415
411
|
useLegacy
|
|
416
412
|
className={`content-template-container ${contentItems?.length > 0 ? 'content-template-container--has-content' : ''}`}
|
package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js
CHANGED
|
@@ -94,7 +94,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
94
94
|
console.log.mockRestore();
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
-
it('empty state: shows
|
|
97
|
+
it('empty state: shows the Channel heading and add-template entry point', () => {
|
|
98
98
|
const onChange = jest.fn();
|
|
99
99
|
renderStep(
|
|
100
100
|
<ChannelSelectionStep
|
|
@@ -104,7 +104,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
104
104
|
/>,
|
|
105
105
|
);
|
|
106
106
|
|
|
107
|
-
expect(screen.getAllByText('
|
|
107
|
+
expect(screen.getAllByText('Channel').length).toBeGreaterThanOrEqual(1);
|
|
108
108
|
expect(screen.getByRole('button', { name: /add creative/i })).toBeInTheDocument();
|
|
109
109
|
expect(screen.queryByTestId('creatives-mock')).not.toBeInTheDocument();
|
|
110
110
|
});
|
|
@@ -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,12 @@ 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 outer `id`, distinct from domainProperties.id (the numeric domainId); it identifies the domain-gateway mapping for the selected domain.
|
|
185
|
+
const getEmailDomainGatewayMapIdByDomainId = (entity, domainId) => {
|
|
186
|
+
const domainEntry = getEmailDomainById(entity, domainId);
|
|
187
|
+
return domainEntry?.id ?? '';
|
|
188
|
+
};
|
|
189
|
+
|
|
184
190
|
/** Domain name for summary display (parseEntityForDisplay) */
|
|
185
191
|
const getEmailDomainNameForDisplay = (entity) => {
|
|
186
192
|
const emailData = getEmailData(entity);
|
|
@@ -262,6 +268,8 @@ const getEmailDefaultsForDomain = (domainData) => {
|
|
|
262
268
|
* context.fieldValues has current form state (e.g. selected emailDomain)
|
|
263
269
|
* Aligned with adiona-ui/cap-campaigns-v2: default = contactInfo item with default:true, or first
|
|
264
270
|
*/
|
|
271
|
+
export { getEmailDomainGatewayMapIdByDomainId };
|
|
272
|
+
|
|
265
273
|
export const getDefaultValueForField = (fieldKey, entity, context = {}) => {
|
|
266
274
|
if (!entity) return '';
|
|
267
275
|
const fieldValues = context?.fieldValues || {};
|
|
@@ -574,7 +582,10 @@ export const parseEntityForDisplay = (entity, context = {}) => {
|
|
|
574
582
|
senderDetails: entity,
|
|
575
583
|
smsSenderId: getSmsSenderIdValue(entity),
|
|
576
584
|
smsDomain: getSmsDomainValue(entity),
|
|
585
|
+
// emailDomain here is the domain NAME (for display only) - buildChannelSettingFromFieldValues needs the numeric domainId/domainGatewayMapId, sourced separately below.
|
|
577
586
|
emailDomain: getEmailDomainNameForDisplay(entity),
|
|
587
|
+
emailDomainId: getEmailDomainValue(entity),
|
|
588
|
+
emailDomainGatewayMapId: getEmailDomainGatewayMapIdByDomainId(entity, getEmailDomainValue(entity)),
|
|
578
589
|
emailSenderId: getEmailSenderIdValue(entity),
|
|
579
590
|
viberSenderId: getSenderIdFromContactInfo(entity, 'VIBER'),
|
|
580
591
|
zaloSenderId: getSenderIdFromContactInfo(entity, 'ZALO'),
|
|
@@ -600,10 +611,12 @@ export const buildChannelSettingFromFieldValues = (fieldValues = {}) => {
|
|
|
600
611
|
}
|
|
601
612
|
if (fieldValues.emailDomain != null || fieldValues.emailSenderId != null || fieldValues.emailSenderName != null || fieldValues.emailReplyToId != null) {
|
|
602
613
|
channelSetting.EMAIL = {
|
|
603
|
-
|
|
604
|
-
|
|
614
|
+
// emailDomainId (numeric, from auto-save defaults via parseEntityForDisplay) takes precedence over emailDomain, which may be a numeric ID in SenderDetails state or a display name from parseEntityForDisplay.
|
|
615
|
+
domainId: fieldValues.emailDomainId ?? fieldValues.emailDomain,
|
|
616
|
+
domainGatewayMapId: fieldValues.emailDomainGatewayMapId,
|
|
617
|
+
senderId: fieldValues.emailSenderId,
|
|
605
618
|
senderLabel: fieldValues.emailSenderName,
|
|
606
|
-
|
|
619
|
+
replyToId: fieldValues.emailReplyToId,
|
|
607
620
|
};
|
|
608
621
|
}
|
|
609
622
|
if (fieldValues.whatsappSenderNumber != null || fieldValues.whatsappAccount != null) {
|
|
@@ -673,9 +686,10 @@ export const parseChannelSettingForDisplay = (channelSetting = {}) => {
|
|
|
673
686
|
}
|
|
674
687
|
if (channelSetting.EMAIL) {
|
|
675
688
|
out.emailDomain = channelSetting.EMAIL.domainId;
|
|
676
|
-
out.
|
|
689
|
+
out.emailDomainGatewayMapId = channelSetting.EMAIL.domainGatewayMapId;
|
|
690
|
+
out.emailSenderId = channelSetting.EMAIL.senderId;
|
|
677
691
|
out.emailSenderName = channelSetting.EMAIL.senderLabel;
|
|
678
|
-
out.emailReplyToId = channelSetting.EMAIL.
|
|
692
|
+
out.emailReplyToId = channelSetting.EMAIL.replyToId;
|
|
679
693
|
}
|
|
680
694
|
if (channelSetting.WHATSAPP) {
|
|
681
695
|
out.whatsappSenderNumber = channelSetting.WHATSAPP.senderMobNum;
|
|
@@ -89,7 +89,7 @@ exports[`Test SlideBoxContent container campaign message, add creative click rcs
|
|
|
89
89
|
/>
|
|
90
90
|
}
|
|
91
91
|
show={true}
|
|
92
|
-
size="size-
|
|
92
|
+
size="size-l"
|
|
93
93
|
/>
|
|
94
94
|
</CreativesSlideBoxWrapper>
|
|
95
95
|
`;
|
|
@@ -183,7 +183,7 @@ exports[`Test SlideBoxContent container campaign message, add creative click wha
|
|
|
183
183
|
/>
|
|
184
184
|
}
|
|
185
185
|
show={true}
|
|
186
|
-
size="size-
|
|
186
|
+
size="size-l"
|
|
187
187
|
/>
|
|
188
188
|
</CreativesSlideBoxWrapper>
|
|
189
189
|
`;
|
|
@@ -320,7 +320,7 @@ exports[`Test SlideBoxContent container campaign message, whatsapp edit all data
|
|
|
320
320
|
/>
|
|
321
321
|
}
|
|
322
322
|
show={false}
|
|
323
|
-
size="size-
|
|
323
|
+
size="size-l"
|
|
324
324
|
/>
|
|
325
325
|
</CreativesSlideBoxWrapper>
|
|
326
326
|
`;
|
|
@@ -457,7 +457,7 @@ exports[`Test SlideBoxContent container campaign message, whatsapp edit min data
|
|
|
457
457
|
/>
|
|
458
458
|
}
|
|
459
459
|
show={false}
|
|
460
|
-
size="size-
|
|
460
|
+
size="size-l"
|
|
461
461
|
/>
|
|
462
462
|
</CreativesSlideBoxWrapper>
|
|
463
463
|
`;
|
|
@@ -594,7 +594,7 @@ exports[`Test SlideBoxContent container it should clear the url, on channel chan
|
|
|
594
594
|
/>
|
|
595
595
|
}
|
|
596
596
|
show={false}
|
|
597
|
-
size="size-
|
|
597
|
+
size="size-l"
|
|
598
598
|
/>
|
|
599
599
|
</CreativesSlideBoxWrapper>
|
|
600
600
|
`;
|