@capillarytech/creatives-library 9.0.54-alpha.3 → 9.0.54
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/services/api.js +0 -9
- package/utils/common.js +4 -0
- package/v2Components/FormBuilder/Classic.js +2 -2
- package/v2Containers/CommunicationFlow/CommunicationFlow.js +58 -100
- package/v2Containers/CommunicationFlow/CommunicationFlowCard.js +5 -21
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +49 -284
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlowCard.test.js +2 -18
- package/v2Containers/CommunicationFlow/constants.js +1 -81
- package/v2Containers/CommunicationFlow/index.js +20 -15
- package/v2Containers/CommunicationFlow/messages.js +0 -4
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.js +0 -6
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +0 -25
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/deliverySettingsConfig.test.js +4 -11
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/deliverySettingsConfig.js +5 -24
- package/v2Containers/MobilePush/Edit/index.js +1 -2
package/constants/unified.js
CHANGED
|
@@ -63,6 +63,7 @@ export const ENABLE_AI_SUGGESTIONS = 'ENABLE_AI_SUGGESTIONS';
|
|
|
63
63
|
export const AI_CONTENT_BOT_DISABLED = 'AI_CONTENT_BOT_DISABLED';
|
|
64
64
|
export const AI_DOCUMENTATION_BOT_ENABLED = 'AI_DOCUMENTATION_BOT_ENABLED';
|
|
65
65
|
export const ENABLE_PRODUCT_SUPPORT_VIDEOS = 'ENABLE_PRODUCT_SUPPORT_VIDEOS';
|
|
66
|
+
export const SUPPORT_ENGAGEMENT_MODULE = 'SUPPORT_ENGAGEMENT_MODULE';
|
|
66
67
|
export const EMBEDDED = 'embedded';
|
|
67
68
|
// --- Tag/Validation Constants ---
|
|
68
69
|
export const CARD_RELATED_TAGS = [
|
package/package.json
CHANGED
package/services/api.js
CHANGED
|
@@ -701,15 +701,6 @@ export const createCentralCommsMetaId = (payload, metaType = TRANSACTION) => {
|
|
|
701
701
|
return request(url, getAPICallObject('POST', payload, false, false, false, true));
|
|
702
702
|
};
|
|
703
703
|
|
|
704
|
-
// CCS CommDefinition API (new Veyron /api/v1/commdefinition surface, proxied
|
|
705
|
-
// through cap-creatives-api's /comm-definitions). Used by CommunicationFlow's
|
|
706
|
-
// own Save action — separate from the legacy messageMeta functions above,
|
|
707
|
-
// which remain in use by CreativesContainer's own save flow (Cap/sagas.js).
|
|
708
|
-
export const createCommDefinition = (payload) => {
|
|
709
|
-
const url = `${API_ENDPOINT}/comm-definitions`;
|
|
710
|
-
return request(url, getAPICallObject('POST', payload, false, false, false, true));
|
|
711
|
-
};
|
|
712
|
-
|
|
713
704
|
export const getCentralCommsMetaIds = (metaIds, metaType = TRANSACTION) => {
|
|
714
705
|
const url = `${API_ENDPOINT}/common/central-comms/meta-id/${metaType}?metaIds=${metaIds}`;
|
|
715
706
|
return request(url, getAPICallObject('GET', null, false, false, false, true));
|
package/utils/common.js
CHANGED
|
@@ -99,6 +99,10 @@ export const hasSupportCKEditor = Auth.hasFeatureAccess.bind(
|
|
|
99
99
|
null,
|
|
100
100
|
SUPPORT_CK_EDITOR,
|
|
101
101
|
);
|
|
102
|
+
export const hasSupportEngagementModule = Auth.hasFeatureAccess.bind(
|
|
103
|
+
null,
|
|
104
|
+
SUPPORT_ENGAGEMENT_MODULE,
|
|
105
|
+
);
|
|
102
106
|
|
|
103
107
|
export const hasGiftVoucherFeature = Auth.hasFeatureAccess.bind(
|
|
104
108
|
null,
|
|
@@ -4276,13 +4276,13 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
4276
4276
|
forceRender: true,
|
|
4277
4277
|
tab: (
|
|
4278
4278
|
<div className="form-tab-header" onInput={(e) => e.stopPropagation()}>
|
|
4279
|
-
{
|
|
4279
|
+
{
|
|
4280
4280
|
_.forEach(renderedContentSections, (value) => {
|
|
4281
4281
|
if (value) {
|
|
4282
4282
|
return value;
|
|
4283
4283
|
}
|
|
4284
4284
|
return value;
|
|
4285
|
-
})
|
|
4285
|
+
})
|
|
4286
4286
|
}
|
|
4287
4287
|
</div>
|
|
4288
4288
|
),
|
|
@@ -17,11 +17,10 @@ import { createStructuredSelector } from 'reselect';
|
|
|
17
17
|
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
18
18
|
import CapDivider from '@capillarytech/cap-ui-library/CapDivider';
|
|
19
19
|
import CapButton from '@capillarytech/cap-ui-library/CapButton';
|
|
20
|
-
import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
|
|
21
20
|
// import injectSaga from '../../utils/injectSaga'; // cap-coupons flows disabled
|
|
22
21
|
// import injectReducer from '../../utils/injectReducer';
|
|
23
22
|
import { makeSelectAuthenticated } from '../Cap/selectors';
|
|
24
|
-
import {
|
|
23
|
+
import { createCentralCommsMetaId, getCentralCommsMetaIds } from '../../services/api';
|
|
25
24
|
import DynamicControlsStep from './steps/DynamicControlsStep';
|
|
26
25
|
import MessageTypeStep from './steps/MessageTypeStep';
|
|
27
26
|
import CommunicationStrategyStep from './steps/CommunicationStrategyStep';
|
|
@@ -36,11 +35,9 @@ import {
|
|
|
36
35
|
CHANNELS,
|
|
37
36
|
INCENTIVE_TYPES,
|
|
38
37
|
DYNAMIC_CONTROLS_CONFIG,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
CCS_CHANNEL_NAME_MAP,
|
|
43
|
-
CCS_CONTENT_TRANSFORMS,
|
|
38
|
+
CHANNEL_CONTENT_KEY_MAP,
|
|
39
|
+
CHANNEL_DELIVERY_KEY_MAP,
|
|
40
|
+
CAMPAIGNS,
|
|
44
41
|
} from './constants';
|
|
45
42
|
import { getEnabledSteps } from './utils/getEnabledSteps';
|
|
46
43
|
import messages from './messages';
|
|
@@ -67,25 +64,6 @@ const hasDeliverySettingForChannel = (channelSetting, channel) => {
|
|
|
67
64
|
return !!settings && Object.values(settings).some((v) => v !== null && v !== '' && v !== undefined);
|
|
68
65
|
};
|
|
69
66
|
|
|
70
|
-
// createCommDefinition resolves (rather than rejects) with the CCS error envelope
|
|
71
|
-
// for 4xx/5xx responses — see api.js's request()/checkStatus. A duplicate
|
|
72
|
-
// referenceId within the org comes back as this specific 409.
|
|
73
|
-
const isDuplicateReferenceIdError = (res) => res?.success === false && res?.status?.message === 'REFERENCE_ID_EXISTS';
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* CCS requires referenceId on create; the consumer-supplied Alert/comm name is
|
|
77
|
-
* mandatory but referenceId is optional in every consumer's own form. Generate
|
|
78
|
-
* a stable fallback from the name rather than failing the save.
|
|
79
|
-
*/
|
|
80
|
-
const buildCcsReferenceId = (name) => {
|
|
81
|
-
const slug = (name || 'COMM')
|
|
82
|
-
.trim()
|
|
83
|
-
.toUpperCase()
|
|
84
|
-
.replace(/[^A-Z0-9]+/g, '_')
|
|
85
|
-
.replace(/^_+|_+$/g, '') || 'COMM';
|
|
86
|
-
return `${slug}_${Date.now()}`;
|
|
87
|
-
};
|
|
88
|
-
|
|
89
67
|
const CommunicationFlow = ({
|
|
90
68
|
config,
|
|
91
69
|
initialData,
|
|
@@ -113,7 +91,6 @@ const CommunicationFlow = ({
|
|
|
113
91
|
};
|
|
114
92
|
});
|
|
115
93
|
const [validationErrors, setValidationErrors] = useState({});
|
|
116
|
-
const [saveError, setSaveError] = useState(null);
|
|
117
94
|
|
|
118
95
|
// Memoize enabled steps
|
|
119
96
|
const enabledSteps = useMemo(() => getEnabledSteps(config), [config]);
|
|
@@ -173,88 +150,74 @@ const CommunicationFlow = ({
|
|
|
173
150
|
}, []);
|
|
174
151
|
|
|
175
152
|
const handleSave = useCallback(async () => {
|
|
176
|
-
setSaveError(null);
|
|
177
153
|
const aggregatedData = getAggregatedData();
|
|
178
154
|
const shouldUseCCS = config?.useCCS !== false;
|
|
179
|
-
let ccsCommDefinition = null;
|
|
180
155
|
|
|
181
156
|
if (shouldUseCCS) {
|
|
182
|
-
const
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
// has no name input of its own, so this is mandatory input from config.context.
|
|
186
|
-
const name = config?.context?.name;
|
|
157
|
+
const ouId = config?.context?.ouId || -1;
|
|
158
|
+
const module = config?.context?.module
|
|
159
|
+
|| (config?.consumer ? config.consumer.toUpperCase() : CAMPAIGNS);
|
|
187
160
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
if (!isMultiChannel && contentItem && name) {
|
|
191
|
-
const rawChannel = (contentItem.channel || '').toUpperCase();
|
|
192
|
-
const channel = CCS_CHANNEL_NAME_MAP[rawChannel] || rawChannel;
|
|
193
|
-
const contentKey = CCS_CHANNEL_CONTENT_KEY_MAP[channel];
|
|
194
|
-
const deliveryKey = CCS_CHANNEL_DELIVERY_KEY_MAP[channel];
|
|
195
|
-
const contentTransform = CCS_CONTENT_TRANSFORMS[channel];
|
|
196
|
-
const contentPayload = contentTransform ? contentTransform(contentItem.templateData) : contentItem.templateData;
|
|
197
|
-
const { dynamicControls = {} } = aggregatedData;
|
|
198
|
-
const channelSettings = aggregatedData.deliverySetting?.channelSetting?.[channel] || {};
|
|
199
|
-
const referenceId = config?.context?.referenceId || buildCcsReferenceId(name);
|
|
161
|
+
const channelContentKeyMap = CHANNEL_CONTENT_KEY_MAP;
|
|
162
|
+
const channelDeliveryKeyMap = CHANNEL_DELIVERY_KEY_MAP;
|
|
200
163
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
name,
|
|
204
|
-
description: config?.context?.description || undefined,
|
|
205
|
-
strategyType: CCS_STRATEGY_TYPE_SINGLE,
|
|
206
|
-
settings: {
|
|
207
|
-
additionalSettings: {
|
|
208
|
-
useTinyUrl: dynamicControls.useTinyUrl ?? false,
|
|
209
|
-
encryptUrl: dynamicControls.sendToControlCustomers ?? false,
|
|
210
|
-
linkTrackingEnabled: dynamicControls.overrideDailyLimit ?? false,
|
|
211
|
-
userSubscriptionDisabled: dynamicControls.sendToBrandPocs ?? false,
|
|
212
|
-
},
|
|
213
|
-
executionParams: {},
|
|
214
|
-
},
|
|
215
|
-
singleChannelStrategy: {
|
|
216
|
-
variant: {
|
|
217
|
-
channel,
|
|
218
|
-
// `channel` spreads LAST in both objects below — contentItem.templateData
|
|
219
|
-
// (e.g. mobile push) carries the UI's own internal channel identifier
|
|
220
|
-
// (MOBILEPUSH), which must never win over CCS's normalized enum value
|
|
221
|
-
// (PUSH) once spread order puts it after.
|
|
222
|
-
...(contentKey && { [contentKey]: { ...contentPayload, channel } }),
|
|
223
|
-
...(deliveryKey && { [deliveryKey]: { channelSettings: { ...channelSettings, channel } } }),
|
|
224
|
-
},
|
|
225
|
-
},
|
|
226
|
-
};
|
|
164
|
+
const contentItems = aggregatedData.contentItems || [];
|
|
165
|
+
const { dynamicControls = {} } = aggregatedData;
|
|
227
166
|
|
|
167
|
+
const additionalSettings = {
|
|
168
|
+
useTinyUrl: dynamicControls.useTinyUrl ?? false,
|
|
169
|
+
encryptUrl: dynamicControls.sendToControlCustomers ?? false,
|
|
170
|
+
linkTrackingEnabled: dynamicControls.overrideDailyLimit ?? false,
|
|
171
|
+
userSubscriptionDisabled: dynamicControls.sendToBrandPocs ?? false,
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
if (contentItems.length > 0) {
|
|
228
175
|
try {
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
176
|
+
const responses = await Promise.all(
|
|
177
|
+
contentItems.map((item) => {
|
|
178
|
+
const channel = (item.channel || '').toUpperCase();
|
|
179
|
+
const contentKey = channelContentKeyMap[channel];
|
|
180
|
+
const deliveryKey = channelDeliveryKeyMap[channel];
|
|
181
|
+
const payload = {
|
|
182
|
+
centralCommsPayload: {
|
|
183
|
+
ouId,
|
|
184
|
+
channel,
|
|
185
|
+
module,
|
|
186
|
+
executionParams: {},
|
|
187
|
+
clientName: 'EMF',
|
|
188
|
+
...(contentKey && {
|
|
189
|
+
[contentKey]: { channel, ...item.templateData },
|
|
190
|
+
}),
|
|
191
|
+
...(deliveryKey && {
|
|
192
|
+
[deliveryKey]: {
|
|
193
|
+
additionalSettings,
|
|
194
|
+
channelSettings: {
|
|
195
|
+
channel,
|
|
196
|
+
...(aggregatedData.deliverySetting?.channelSetting?.[channel] || {}),
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
}),
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
return createCentralCommsMetaId(payload);
|
|
203
|
+
}),
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const metaIds = responses
|
|
207
|
+
.map((res) => res?.response?.data?.id)
|
|
208
|
+
.filter(Boolean)
|
|
209
|
+
.join(',');
|
|
210
|
+
|
|
211
|
+
if (metaIds) {
|
|
212
|
+
const getResponse = await getCentralCommsMetaIds(metaIds);
|
|
244
213
|
}
|
|
245
214
|
} catch (error) {
|
|
246
|
-
console.error('[CommunicationFlow] CCS
|
|
215
|
+
console.error('[CommunicationFlow] CCS createCentralCommsMetaId error:', error);
|
|
247
216
|
}
|
|
248
|
-
} else if (!name) {
|
|
249
|
-
console.warn('[CommunicationFlow] Skipping CCS createCommDefinition — config.context.name is required');
|
|
250
|
-
} else if (isMultiChannel) {
|
|
251
|
-
console.warn('[CommunicationFlow] Skipping CCS createCommDefinition — CHANNEL_PRIORITY/AB_TEST strategies are not supported by CCS create yet (SINGLE only)');
|
|
252
|
-
} else if (!contentItem) {
|
|
253
|
-
console.warn('[CommunicationFlow] Skipping CCS createCommDefinition — no content item found (contentItems is empty)');
|
|
254
217
|
}
|
|
255
218
|
}
|
|
256
219
|
|
|
257
|
-
onSave(
|
|
220
|
+
onSave(aggregatedData);
|
|
258
221
|
}, [getAggregatedData, config, onSave]);
|
|
259
222
|
|
|
260
223
|
// Call onChange callback when stepData changes
|
|
@@ -354,11 +317,6 @@ const CommunicationFlow = ({
|
|
|
354
317
|
{renderSteps()}
|
|
355
318
|
{onSave && (
|
|
356
319
|
<CapRow useLegacy className="communication-flow-container__footer">
|
|
357
|
-
{saveError && (
|
|
358
|
-
<CapLabel type="label2" className="communication-flow-container__save-error">
|
|
359
|
-
{saveError}
|
|
360
|
-
</CapLabel>
|
|
361
|
-
)}
|
|
362
320
|
<CapButton type="primary" onClick={handleSave} disabled={isSaveDisabled}>
|
|
363
321
|
{formatMessage(messages.save)}
|
|
364
322
|
</CapButton>
|
|
@@ -19,7 +19,6 @@ import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
|
|
|
19
19
|
import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
|
|
20
20
|
import CapHeader from '@capillarytech/cap-ui-library/CapHeader';
|
|
21
21
|
import CapSlideBox from '@capillarytech/cap-ui-library/CapSlideBox';
|
|
22
|
-
import CapTooltip from '@capillarytech/cap-ui-library/CapTooltip';
|
|
23
22
|
import CommunicationFlow from './index';
|
|
24
23
|
import { CHANNELS, DEFAULT_COMMUNICATION_STRATEGY_OPTIONS, DYNAMIC_CONTROLS_CONFIG } from './constants';
|
|
25
24
|
import {
|
|
@@ -42,7 +41,7 @@ const resolveValue = (val) => {
|
|
|
42
41
|
|
|
43
42
|
const SENDER_ID_RESOLVERS = {
|
|
44
43
|
[SMS]: (setting) => resolveValue(setting?.gsmSenderId),
|
|
45
|
-
[EMAIL]: (setting) => resolveValue(setting?.
|
|
44
|
+
[EMAIL]: (setting) => resolveValue(setting?.senderEmail),
|
|
46
45
|
[VIBER]: (setting) => resolveValue(setting?.sender) || resolveValue(setting?.gsmSenderId),
|
|
47
46
|
[WHATSAPP]: (setting) => resolveValue(setting?.senderMobNum),
|
|
48
47
|
[RCS]: (setting) => resolveValue(setting?.senderMobNum) || resolveValue(setting?.rcsSender),
|
|
@@ -75,8 +74,6 @@ const CommunicationFlowCard = ({
|
|
|
75
74
|
onChange,
|
|
76
75
|
cap,
|
|
77
76
|
intl,
|
|
78
|
-
disabled,
|
|
79
|
-
disabledTooltip,
|
|
80
77
|
}) => {
|
|
81
78
|
const { formatMessage } = intl || {};
|
|
82
79
|
const [showSlideBox, setShowSlideBox] = useState(false);
|
|
@@ -94,9 +91,8 @@ const CommunicationFlowCard = ({
|
|
|
94
91
|
}, [onCancel]);
|
|
95
92
|
|
|
96
93
|
const handleOpen = useCallback(() => {
|
|
97
|
-
if (disabled) return;
|
|
98
94
|
setShowSlideBox(true);
|
|
99
|
-
}, [
|
|
95
|
+
}, []);
|
|
100
96
|
|
|
101
97
|
const firstItem = savedData?.contentItems?.[0];
|
|
102
98
|
const channelConfig = firstItem
|
|
@@ -186,17 +182,9 @@ const CommunicationFlowCard = ({
|
|
|
186
182
|
<CapImage src={addCreativesIllustration} />
|
|
187
183
|
</CapColumn>
|
|
188
184
|
<CapColumn span={14} className="empty-card-action-col">
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
{formatMessage(messages.addCreatives)}
|
|
193
|
-
</CapButton>
|
|
194
|
-
</CapTooltip>
|
|
195
|
-
) : (
|
|
196
|
-
<CapButton type="secondary" onClick={handleOpen} disabled={disabled}>
|
|
197
|
-
{formatMessage(messages.addCreatives)}
|
|
198
|
-
</CapButton>
|
|
199
|
-
)}
|
|
185
|
+
<CapButton type="secondary" onClick={handleOpen}>
|
|
186
|
+
{formatMessage(messages.addCreatives)}
|
|
187
|
+
</CapButton>
|
|
200
188
|
</CapColumn>
|
|
201
189
|
</CapRow>
|
|
202
190
|
</CapCard>
|
|
@@ -239,8 +227,6 @@ CommunicationFlowCard.propTypes = {
|
|
|
239
227
|
onChange: PropTypes.func,
|
|
240
228
|
cap: PropTypes.object,
|
|
241
229
|
intl: PropTypes.object.isRequired,
|
|
242
|
-
disabled: PropTypes.bool, // Disables the "Add creatives" trigger (e.g. until a consumer-required field is filled)
|
|
243
|
-
disabledTooltip: PropTypes.node, // Shown on hover when disabled is true
|
|
244
230
|
};
|
|
245
231
|
|
|
246
232
|
CommunicationFlowCard.defaultProps = {
|
|
@@ -249,8 +235,6 @@ CommunicationFlowCard.defaultProps = {
|
|
|
249
235
|
onCancel: null,
|
|
250
236
|
onChange: null,
|
|
251
237
|
cap: null,
|
|
252
|
-
disabled: false,
|
|
253
|
-
disabledTooltip: null,
|
|
254
238
|
};
|
|
255
239
|
|
|
256
240
|
export default injectIntl(CommunicationFlowCard);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
3
|
jest.mock('../../../services/api', () => ({
|
|
4
|
-
|
|
4
|
+
createCentralCommsMetaId: jest.fn(),
|
|
5
|
+
getCentralCommsMetaIds: jest.fn(),
|
|
5
6
|
}));
|
|
6
7
|
|
|
7
8
|
jest.mock('../../CreativesContainer', () => function MockCreativesContainer({
|
|
@@ -37,7 +38,7 @@ import { IntlProvider } from 'react-intl';
|
|
|
37
38
|
import history from '../../../utils/history';
|
|
38
39
|
import { initialReducer } from '../../../initialReducer';
|
|
39
40
|
import CommunicationFlow from '../CommunicationFlow';
|
|
40
|
-
import {
|
|
41
|
+
import { createCentralCommsMetaId, getCentralCommsMetaIds } from '../../../services/api';
|
|
41
42
|
import { getEnabledSteps } from '../utils/getEnabledSteps';
|
|
42
43
|
import {
|
|
43
44
|
CHANNELS,
|
|
@@ -358,241 +359,82 @@ describe('isSaveDisabled', () => {
|
|
|
358
359
|
});
|
|
359
360
|
|
|
360
361
|
describe('handleSave — CCS flow', () => {
|
|
361
|
-
const ccsConfig = { ...baseConfig, context: { name: 'Order Placed Notification' }, features: {} };
|
|
362
|
-
|
|
363
362
|
beforeEach(() => {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
});
|
|
363
|
+
createCentralCommsMetaId.mockResolvedValue({ response: { data: { id: 'meta-123' } } });
|
|
364
|
+
getCentralCommsMetaIds.mockResolvedValue({ response: { data: {} } });
|
|
367
365
|
});
|
|
368
366
|
|
|
369
367
|
afterEach(() => {
|
|
370
368
|
jest.clearAllMocks();
|
|
371
369
|
});
|
|
372
370
|
|
|
373
|
-
it('calls
|
|
371
|
+
it('calls createCentralCommsMetaId for each content item when useCCS is not false', async () => {
|
|
374
372
|
const onSave = jest.fn();
|
|
375
373
|
renderWithFlow({
|
|
376
374
|
features: {},
|
|
377
|
-
config: ccsConfig,
|
|
378
375
|
initialData: {
|
|
379
|
-
contentItems: [{ channel: 'sms', templateData: {
|
|
376
|
+
contentItems: [{ channel: 'sms', templateData: { smsBody: 'Hello' } }],
|
|
380
377
|
},
|
|
381
378
|
onSave,
|
|
382
379
|
});
|
|
383
380
|
|
|
384
381
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
385
382
|
|
|
386
|
-
await waitFor(() => expect(
|
|
387
|
-
expect(
|
|
383
|
+
await waitFor(() => expect(createCentralCommsMetaId).toHaveBeenCalledTimes(1));
|
|
384
|
+
expect(createCentralCommsMetaId).toHaveBeenCalledWith(
|
|
388
385
|
expect.objectContaining({
|
|
389
|
-
|
|
390
|
-
strategyType: 'SINGLE',
|
|
391
|
-
singleChannelStrategy: expect.objectContaining({
|
|
392
|
-
variant: expect.objectContaining({
|
|
393
|
-
channel: 'SMS',
|
|
394
|
-
smsMessageContent: { channel: 'SMS', message: 'Hello' },
|
|
395
|
-
}),
|
|
396
|
-
}),
|
|
386
|
+
centralCommsPayload: expect.objectContaining({ channel: 'SMS', module: 'CAMPAIGNS' }),
|
|
397
387
|
}),
|
|
398
388
|
);
|
|
399
389
|
expect(onSave).toHaveBeenCalledTimes(1);
|
|
400
390
|
});
|
|
401
391
|
|
|
402
|
-
it('
|
|
403
|
-
const onSave = jest.fn();
|
|
404
|
-
renderWithFlow({
|
|
405
|
-
features: {},
|
|
406
|
-
config: ccsConfig,
|
|
407
|
-
initialData: {
|
|
408
|
-
contentItems: [{
|
|
409
|
-
channel: 'EMAIL',
|
|
410
|
-
templateData: { emailSubject: 'Order Confirmed', emailBody: '<html>Hi</html>' },
|
|
411
|
-
}],
|
|
412
|
-
},
|
|
413
|
-
onSave,
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
417
|
-
|
|
418
|
-
await waitFor(() => expect(createCommDefinition).toHaveBeenCalledTimes(1));
|
|
419
|
-
expect(createCommDefinition).toHaveBeenCalledWith(
|
|
420
|
-
expect.objectContaining({
|
|
421
|
-
singleChannelStrategy: expect.objectContaining({
|
|
422
|
-
variant: expect.objectContaining({
|
|
423
|
-
channel: 'EMAIL',
|
|
424
|
-
emailMessageContent: {
|
|
425
|
-
channel: 'EMAIL',
|
|
426
|
-
messageSubject: 'Order Confirmed',
|
|
427
|
-
messageBody: '<html>Hi</html>',
|
|
428
|
-
},
|
|
429
|
-
}),
|
|
430
|
-
}),
|
|
431
|
-
}),
|
|
432
|
-
);
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
it('transforms WEBPUSH content from the legacy editor\'s messageContent.content wrapping to CCS\'s flat shape', async () => {
|
|
436
|
-
const onSave = jest.fn();
|
|
437
|
-
const cta = { type: 'SITE_URL', actionLink: 'https://example.com' };
|
|
438
|
-
const expandableDetails = { media: [{ url: 'https://example.com/banner.png', type: 'IMAGE' }], ctas: [] };
|
|
439
|
-
renderWithFlow({
|
|
440
|
-
features: {},
|
|
441
|
-
config: ccsConfig,
|
|
442
|
-
initialData: {
|
|
443
|
-
contentItems: [{
|
|
444
|
-
channel: 'WEBPUSH',
|
|
445
|
-
templateData: {
|
|
446
|
-
messageContent: {
|
|
447
|
-
content: {
|
|
448
|
-
channel: 'WEBPUSH',
|
|
449
|
-
accountId: 13792,
|
|
450
|
-
content: { title: 'Hi', message: 'Body', iconImageUrl: 'https://example.com/icon.png', cta, expandableDetails },
|
|
451
|
-
messageSubject: 'Subject',
|
|
452
|
-
offers: [],
|
|
453
|
-
},
|
|
454
|
-
},
|
|
455
|
-
},
|
|
456
|
-
}],
|
|
457
|
-
},
|
|
458
|
-
onSave,
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
462
|
-
|
|
463
|
-
await waitFor(() => expect(createCommDefinition).toHaveBeenCalledTimes(1));
|
|
464
|
-
expect(createCommDefinition).toHaveBeenCalledWith(
|
|
465
|
-
expect.objectContaining({
|
|
466
|
-
singleChannelStrategy: expect.objectContaining({
|
|
467
|
-
variant: expect.objectContaining({
|
|
468
|
-
channel: 'WEBPUSH',
|
|
469
|
-
webPushMessageContent: {
|
|
470
|
-
channel: 'WEBPUSH',
|
|
471
|
-
messageSubject: 'Subject',
|
|
472
|
-
accountId: 13792,
|
|
473
|
-
content: { title: 'Hi', message: 'Body', iconImageUrl: 'https://example.com/icon.png', cta, expandableDetails },
|
|
474
|
-
},
|
|
475
|
-
}),
|
|
476
|
-
}),
|
|
477
|
-
}),
|
|
478
|
-
);
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
it('normalizes the internal MOBILEPUSH channel to CCS\'s PUSH enum (keys stay mpush*), even when templateData/channelSetting carry their own stray channel field', async () => {
|
|
482
|
-
const onSave = jest.fn();
|
|
392
|
+
it('calls getCentralCommsMetaIds when metaIds are returned from createCentralCommsMetaId', async () => {
|
|
483
393
|
renderWithFlow({
|
|
484
394
|
features: {},
|
|
485
|
-
|
|
486
|
-
initialData: {
|
|
487
|
-
// `channel: 'MOBILEPUSH'` inline on templateData reproduces the real
|
|
488
|
-
// production bug: contentItem.templateData carries the UI's own
|
|
489
|
-
// internal channel identifier, which must not win over the
|
|
490
|
-
// normalized 'PUSH' enum once spread into mpushMessageContent.
|
|
491
|
-
contentItems: [{ channel: 'MOBILEPUSH', templateData: { channel: 'MOBILEPUSH', messageSubject: 'Hi' } }],
|
|
492
|
-
// Likewise, deliverySetting.channelSetting.PUSH carrying its own
|
|
493
|
-
// stray `channel` key must not win over the normalized value once
|
|
494
|
-
// spread into mpushDeliverySettings.channelSettings.
|
|
495
|
-
deliverySetting: { channelSetting: { PUSH: { channel: 'MOBILEPUSH', domainId: 2002 } } },
|
|
496
|
-
},
|
|
497
|
-
onSave,
|
|
395
|
+
initialData: { contentItems: [{ channel: 'EMAIL', templateData: {} }] },
|
|
498
396
|
});
|
|
499
397
|
|
|
500
398
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
501
399
|
|
|
502
|
-
await waitFor(() => expect(
|
|
503
|
-
expect(createCommDefinition).toHaveBeenCalledWith(
|
|
504
|
-
expect.objectContaining({
|
|
505
|
-
singleChannelStrategy: expect.objectContaining({
|
|
506
|
-
variant: expect.objectContaining({
|
|
507
|
-
channel: 'PUSH',
|
|
508
|
-
mpushMessageContent: { channel: 'PUSH', messageSubject: 'Hi' },
|
|
509
|
-
mpushDeliverySettings: {
|
|
510
|
-
channelSettings: { channel: 'PUSH', domainId: 2002 },
|
|
511
|
-
},
|
|
512
|
-
}),
|
|
513
|
-
}),
|
|
514
|
-
}),
|
|
515
|
-
);
|
|
400
|
+
await waitFor(() => expect(getCentralCommsMetaIds).toHaveBeenCalledWith('meta-123'));
|
|
516
401
|
});
|
|
517
402
|
|
|
518
|
-
it('
|
|
519
|
-
|
|
403
|
+
it('skips getCentralCommsMetaIds when response contains no id', async () => {
|
|
404
|
+
createCentralCommsMetaId.mockResolvedValue({ response: { data: {} } });
|
|
520
405
|
renderWithFlow({
|
|
521
406
|
features: {},
|
|
522
|
-
config: ccsConfig,
|
|
523
407
|
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
524
|
-
onSave,
|
|
525
408
|
});
|
|
526
409
|
|
|
527
410
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
528
411
|
|
|
529
|
-
await waitFor(() => expect(
|
|
530
|
-
expect(
|
|
531
|
-
expect.objectContaining({
|
|
532
|
-
ccsCommDefinition: { id: 'cd_123', referenceId: 'ORDER_PLACED_123', version: 1, status: 'DRAFT' },
|
|
533
|
-
}),
|
|
534
|
-
);
|
|
412
|
+
await waitFor(() => expect(createCentralCommsMetaId).toHaveBeenCalled());
|
|
413
|
+
expect(getCentralCommsMetaIds).not.toHaveBeenCalled();
|
|
535
414
|
});
|
|
536
415
|
|
|
537
|
-
it('
|
|
538
|
-
const onSave = jest.fn();
|
|
539
|
-
createCommDefinition.mockResolvedValueOnce({
|
|
540
|
-
response: { data: { id: 'cd_123', referenceId: 'ORDER_PLACED_123', status: 'DRAFT', version: { version: 0 } } },
|
|
541
|
-
});
|
|
416
|
+
it('uses ouId and module from config.context when provided', async () => {
|
|
542
417
|
renderWithFlow({
|
|
543
418
|
features: {},
|
|
544
|
-
config:
|
|
419
|
+
config: { ...baseConfig, context: { ouId: 42, module: 'LOYALTY' }, features: {} },
|
|
545
420
|
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
546
|
-
onSave,
|
|
547
421
|
});
|
|
548
422
|
|
|
549
423
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
550
424
|
|
|
551
|
-
await waitFor(() => expect(
|
|
552
|
-
expect(
|
|
425
|
+
await waitFor(() => expect(createCentralCommsMetaId).toHaveBeenCalled());
|
|
426
|
+
expect(createCentralCommsMetaId).toHaveBeenCalledWith(
|
|
553
427
|
expect.objectContaining({
|
|
554
|
-
|
|
428
|
+
centralCommsPayload: expect.objectContaining({ ouId: 42, module: 'LOYALTY' }),
|
|
555
429
|
}),
|
|
556
430
|
);
|
|
557
431
|
});
|
|
558
432
|
|
|
559
|
-
it('generates a referenceId from name when config.context.referenceId is absent', async () => {
|
|
560
|
-
// moduleMocks.js spies on Date.now globally, but this config's resetMocks:true
|
|
561
|
-
// clears that return value before every test — set it explicitly here.
|
|
562
|
-
jest.spyOn(Date, 'now').mockReturnValue(1612267539410);
|
|
563
|
-
renderWithFlow({
|
|
564
|
-
features: {},
|
|
565
|
-
config: ccsConfig,
|
|
566
|
-
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
567
|
-
});
|
|
568
|
-
|
|
569
|
-
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
570
|
-
|
|
571
|
-
await waitFor(() => expect(createCommDefinition).toHaveBeenCalled());
|
|
572
|
-
const payload = createCommDefinition.mock.calls[0][0];
|
|
573
|
-
expect(payload.referenceId).toBe('ORDER_PLACED_NOTIFICATION_1612267539410');
|
|
574
|
-
});
|
|
575
|
-
|
|
576
|
-
it('uses config.context.referenceId and description when provided', async () => {
|
|
577
|
-
renderWithFlow({
|
|
578
|
-
features: {},
|
|
579
|
-
config: { ...ccsConfig, context: { ...ccsConfig.context, referenceId: 'ORDER_PLACED', description: 'desc' } },
|
|
580
|
-
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
581
|
-
});
|
|
582
|
-
|
|
583
|
-
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
584
|
-
|
|
585
|
-
await waitFor(() => expect(createCommDefinition).toHaveBeenCalled());
|
|
586
|
-
expect(createCommDefinition).toHaveBeenCalledWith(
|
|
587
|
-
expect.objectContaining({ referenceId: 'ORDER_PLACED', description: 'desc' }),
|
|
588
|
-
);
|
|
589
|
-
});
|
|
590
|
-
|
|
591
433
|
it('skips CCS entirely when useCCS is false', async () => {
|
|
592
434
|
const onSave = jest.fn();
|
|
593
435
|
renderWithFlow({
|
|
594
436
|
features: {},
|
|
595
|
-
config: { ...
|
|
437
|
+
config: { ...baseConfig, useCCS: false, features: {} },
|
|
596
438
|
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
597
439
|
onSave,
|
|
598
440
|
});
|
|
@@ -600,15 +442,14 @@ describe('handleSave — CCS flow', () => {
|
|
|
600
442
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
601
443
|
|
|
602
444
|
await waitFor(() => expect(onSave).toHaveBeenCalledTimes(1));
|
|
603
|
-
expect(
|
|
604
|
-
expect(onSave).toHaveBeenCalledWith(expect.not.objectContaining({ ccsCommDefinition: expect.anything() }));
|
|
445
|
+
expect(createCentralCommsMetaId).not.toHaveBeenCalled();
|
|
605
446
|
});
|
|
606
447
|
|
|
607
|
-
it('
|
|
448
|
+
it('still calls onSave when createCentralCommsMetaId rejects', async () => {
|
|
449
|
+
createCentralCommsMetaId.mockRejectedValue(new Error('Network error'));
|
|
608
450
|
const onSave = jest.fn();
|
|
609
451
|
renderWithFlow({
|
|
610
452
|
features: {},
|
|
611
|
-
config: { ...baseConfig, features: {} },
|
|
612
453
|
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
613
454
|
onSave,
|
|
614
455
|
});
|
|
@@ -616,70 +457,12 @@ describe('handleSave — CCS flow', () => {
|
|
|
616
457
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
617
458
|
|
|
618
459
|
await waitFor(() => expect(onSave).toHaveBeenCalledTimes(1));
|
|
619
|
-
expect(createCommDefinition).not.toHaveBeenCalled();
|
|
620
|
-
});
|
|
621
|
-
|
|
622
|
-
it('skips createCommDefinition for multi-channel strategies (CHANNEL_PRIORITY/AB_TEST)', async () => {
|
|
623
|
-
const onSave = jest.fn();
|
|
624
|
-
renderWithFlow({
|
|
625
|
-
features: {},
|
|
626
|
-
config: ccsConfig,
|
|
627
|
-
initialData: {
|
|
628
|
-
communicationStrategy: CHANNEL_PRIORITY,
|
|
629
|
-
channels: ['SMS', 'EMAIL'],
|
|
630
|
-
contentItems: [{ channel: 'SMS', templateData: {} }, { channel: 'EMAIL', templateData: {} }],
|
|
631
|
-
},
|
|
632
|
-
onSave,
|
|
633
|
-
});
|
|
634
|
-
|
|
635
|
-
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
636
|
-
|
|
637
|
-
await waitFor(() => expect(onSave).toHaveBeenCalledTimes(1));
|
|
638
|
-
expect(createCommDefinition).not.toHaveBeenCalled();
|
|
639
460
|
});
|
|
640
461
|
|
|
641
|
-
it('
|
|
642
|
-
createCommDefinition.mockRejectedValue(new Error('Network error'));
|
|
462
|
+
it('skips createCentralCommsMetaId when contentItems is empty', async () => {
|
|
643
463
|
const onSave = jest.fn();
|
|
644
464
|
renderWithFlow({
|
|
645
465
|
features: {},
|
|
646
|
-
config: ccsConfig,
|
|
647
|
-
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
648
|
-
onSave,
|
|
649
|
-
});
|
|
650
|
-
|
|
651
|
-
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
652
|
-
|
|
653
|
-
await waitFor(() => expect(onSave).toHaveBeenCalledTimes(1));
|
|
654
|
-
expect(onSave).toHaveBeenCalledWith(expect.not.objectContaining({ ccsCommDefinition: expect.anything() }));
|
|
655
|
-
});
|
|
656
|
-
|
|
657
|
-
it('blocks save and shows an error when createCommDefinition resolves with a duplicate REFERENCE_ID_EXISTS conflict', async () => {
|
|
658
|
-
createCommDefinition.mockResolvedValue({
|
|
659
|
-
success: false,
|
|
660
|
-
status: { isError: true, code: 409, message: 'REFERENCE_ID_EXISTS' },
|
|
661
|
-
message: 'REFERENCE_ID_EXISTS',
|
|
662
|
-
});
|
|
663
|
-
const onSave = jest.fn();
|
|
664
|
-
renderWithFlow({
|
|
665
|
-
features: {},
|
|
666
|
-
config: ccsConfig,
|
|
667
|
-
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
668
|
-
onSave,
|
|
669
|
-
});
|
|
670
|
-
|
|
671
|
-
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
672
|
-
|
|
673
|
-
await waitFor(() => expect(createCommDefinition).toHaveBeenCalledTimes(1));
|
|
674
|
-
expect(await screen.findByText(/already in use in your organization/i)).toBeInTheDocument();
|
|
675
|
-
expect(onSave).not.toHaveBeenCalled();
|
|
676
|
-
});
|
|
677
|
-
|
|
678
|
-
it('skips createCommDefinition when contentItems is empty', async () => {
|
|
679
|
-
const onSave = jest.fn();
|
|
680
|
-
renderWithFlow({
|
|
681
|
-
features: {},
|
|
682
|
-
config: ccsConfig,
|
|
683
466
|
initialData: { contentItems: [] },
|
|
684
467
|
onSave,
|
|
685
468
|
});
|
|
@@ -687,13 +470,12 @@ describe('handleSave — CCS flow', () => {
|
|
|
687
470
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
688
471
|
|
|
689
472
|
await waitFor(() => expect(onSave).toHaveBeenCalledTimes(1));
|
|
690
|
-
expect(
|
|
473
|
+
expect(createCentralCommsMetaId).not.toHaveBeenCalled();
|
|
691
474
|
});
|
|
692
475
|
|
|
693
|
-
it('
|
|
476
|
+
it('includes additionalSettings derived from dynamicControls in the payload', async () => {
|
|
694
477
|
renderWithFlow({
|
|
695
478
|
features: {},
|
|
696
|
-
config: ccsConfig,
|
|
697
479
|
initialData: {
|
|
698
480
|
contentItems: [{ channel: 'SMS', templateData: {} }],
|
|
699
481
|
dynamicControls: {
|
|
@@ -707,36 +489,14 @@ describe('handleSave — CCS flow', () => {
|
|
|
707
489
|
|
|
708
490
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
709
491
|
|
|
710
|
-
await waitFor(() => expect(
|
|
711
|
-
const payload =
|
|
712
|
-
expect(payload.
|
|
492
|
+
await waitFor(() => expect(createCentralCommsMetaId).toHaveBeenCalled());
|
|
493
|
+
const payload = createCentralCommsMetaId.mock.calls[0][0];
|
|
494
|
+
expect(payload.centralCommsPayload.smsDeliverySettings.additionalSettings).toEqual({
|
|
713
495
|
useTinyUrl: true,
|
|
714
496
|
encryptUrl: true,
|
|
715
497
|
linkTrackingEnabled: true,
|
|
716
498
|
userSubscriptionDisabled: true,
|
|
717
499
|
});
|
|
718
|
-
expect(payload.singleChannelStrategy.variant.smsDeliverySettings.additionalSettings).toBeUndefined();
|
|
719
|
-
});
|
|
720
|
-
|
|
721
|
-
it('includes channelSettings from deliverySetting.channelSetting in the payload', async () => {
|
|
722
|
-
renderWithFlow({
|
|
723
|
-
features: {},
|
|
724
|
-
config: ccsConfig,
|
|
725
|
-
initialData: {
|
|
726
|
-
contentItems: [{ channel: 'SMS', templateData: {} }],
|
|
727
|
-
deliverySetting: { channelSetting: { SMS: { domainId: 1001, gsmSenderId: 'BRAND1' } } },
|
|
728
|
-
},
|
|
729
|
-
});
|
|
730
|
-
|
|
731
|
-
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
732
|
-
|
|
733
|
-
await waitFor(() => expect(createCommDefinition).toHaveBeenCalled());
|
|
734
|
-
const payload = createCommDefinition.mock.calls[0][0];
|
|
735
|
-
expect(payload.singleChannelStrategy.variant.smsDeliverySettings.channelSettings).toEqual({
|
|
736
|
-
channel: 'SMS',
|
|
737
|
-
domainId: 1001,
|
|
738
|
-
gsmSenderId: 'BRAND1',
|
|
739
|
-
});
|
|
740
500
|
});
|
|
741
501
|
});
|
|
742
502
|
|
|
@@ -792,34 +552,39 @@ describe('optional chaining safety', () => {
|
|
|
792
552
|
jest.clearAllMocks();
|
|
793
553
|
});
|
|
794
554
|
|
|
795
|
-
it('
|
|
796
|
-
|
|
555
|
+
it('defaults ouId to -1 when config.context is absent', async () => {
|
|
556
|
+
createCentralCommsMetaId.mockResolvedValue({ response: { data: { id: 'x' } } });
|
|
797
557
|
renderWithFlow({
|
|
798
558
|
features: {},
|
|
799
|
-
config: { ...baseConfig, features: {} },
|
|
800
559
|
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
801
|
-
onSave,
|
|
802
560
|
});
|
|
803
561
|
|
|
804
562
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
805
563
|
|
|
806
|
-
await waitFor(() => expect(
|
|
807
|
-
expect(
|
|
564
|
+
await waitFor(() => expect(createCentralCommsMetaId).toHaveBeenCalled());
|
|
565
|
+
expect(createCentralCommsMetaId).toHaveBeenCalledWith(
|
|
566
|
+
expect.objectContaining({
|
|
567
|
+
centralCommsPayload: expect.objectContaining({ ouId: -1 }),
|
|
568
|
+
}),
|
|
569
|
+
);
|
|
808
570
|
});
|
|
809
571
|
|
|
810
|
-
it('
|
|
811
|
-
|
|
572
|
+
it('defaults module to consumer.toUpperCase() when config.context.module absent', async () => {
|
|
573
|
+
createCentralCommsMetaId.mockResolvedValue({ response: { data: { id: 'x' } } });
|
|
812
574
|
renderWithFlow({
|
|
813
575
|
features: {},
|
|
814
|
-
config: { ...baseConfig,
|
|
576
|
+
config: { ...baseConfig, consumer: 'loyalty', features: {} },
|
|
815
577
|
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
816
|
-
onSave,
|
|
817
578
|
});
|
|
818
579
|
|
|
819
580
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
820
581
|
|
|
821
|
-
await waitFor(() => expect(
|
|
822
|
-
expect(
|
|
582
|
+
await waitFor(() => expect(createCentralCommsMetaId).toHaveBeenCalled());
|
|
583
|
+
expect(createCentralCommsMetaId).toHaveBeenCalledWith(
|
|
584
|
+
expect.objectContaining({
|
|
585
|
+
centralCommsPayload: expect.objectContaining({ module: 'LOYALTY' }),
|
|
586
|
+
}),
|
|
587
|
+
);
|
|
823
588
|
});
|
|
824
589
|
|
|
825
590
|
it('initializes channel from config.channel when initialData.channel is absent', () => {
|
|
@@ -108,22 +108,6 @@ 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
|
-
});
|
|
127
111
|
});
|
|
128
112
|
|
|
129
113
|
describe('configured state (initialData provided)', () => {
|
|
@@ -247,11 +231,11 @@ describe('CommunicationFlowCard', () => {
|
|
|
247
231
|
expect(screen.getByText(/Sender ID/)).toBeInTheDocument();
|
|
248
232
|
});
|
|
249
233
|
|
|
250
|
-
it('shows
|
|
234
|
+
it('shows senderEmail for EMAIL with Sender ID label', () => {
|
|
251
235
|
renderCard({
|
|
252
236
|
initialData: makeSavedData({
|
|
253
237
|
contentItems: [{ channel: 'EMAIL', templateData: {} }],
|
|
254
|
-
deliverySetting: { channelSetting: { EMAIL: {
|
|
238
|
+
deliverySetting: { channelSetting: { EMAIL: { senderEmail: 'test@example.com' } } },
|
|
255
239
|
}),
|
|
256
240
|
});
|
|
257
241
|
expect(screen.getByText(/test@example.com/)).toBeInTheDocument();
|
|
@@ -38,86 +38,6 @@ 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
|
-
// WEBPUSH: the legacy editor nests the real data under
|
|
82
|
-
// messageContent.content (see CommunicationFlowCard.js's own comment on
|
|
83
|
-
// this same wrapping, for the edit-mode case) — extracting it flat here
|
|
84
|
-
// matches CCS's shape ({ messageSubject, accountId, content }); `offers`
|
|
85
|
-
// (a legacy-only field) is deliberately dropped, not part of CCS's schema.
|
|
86
|
-
WEBPUSH: (templateData = {}) => {
|
|
87
|
-
const inner = templateData.messageContent?.content || templateData;
|
|
88
|
-
return {
|
|
89
|
-
messageSubject: inner.messageSubject,
|
|
90
|
-
accountId: inner.accountId,
|
|
91
|
-
content: inner.content,
|
|
92
|
-
};
|
|
93
|
-
},
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
export const CCS_CHANNEL_DELIVERY_KEY_MAP = {
|
|
97
|
-
SMS: 'smsDeliverySettings',
|
|
98
|
-
EMAIL: 'emailDeliverySettings',
|
|
99
|
-
WHATSAPP: 'whatsappDeliverySettings',
|
|
100
|
-
PUSH: 'mpushDeliverySettings',
|
|
101
|
-
INAPP: 'inAppDeliverySettings',
|
|
102
|
-
ANDROID: 'androidDeliverySettings',
|
|
103
|
-
IOS: 'iosDeliverySettings',
|
|
104
|
-
ZALO: 'zaloDeliverySettings',
|
|
105
|
-
VIBER: 'viberDeliverySettings',
|
|
106
|
-
LINE: 'lineDeliverySettings',
|
|
107
|
-
WEBPUSH: 'webPushDeliverySettings',
|
|
108
|
-
RCS: 'rcsDeliverySettings',
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
// The UI's own internal channel identifier for mobile push is 'MOBILEPUSH'
|
|
112
|
-
// (see CreativesContainer/constants.js MOBILE_PUSH), but CCS's channel enum
|
|
113
|
-
// value is 'PUSH' (verified against real create responses — the content/
|
|
114
|
-
// delivery KEYS still say 'mpush*', only the enum VALUE is 'PUSH') —
|
|
115
|
-
// normalize before building the CCS payload. Every other channel identifier
|
|
116
|
-
// matches CCS as-is.
|
|
117
|
-
export const CCS_CHANNEL_NAME_MAP = {
|
|
118
|
-
MOBILEPUSH: 'PUSH',
|
|
119
|
-
};
|
|
120
|
-
|
|
121
41
|
// Channel config - single source of truth for CreativesContainer and TemplatesV2
|
|
122
42
|
// paneKey: TemplatesV2 defaultPanes object key (for channelsToHide)
|
|
123
43
|
// channelProp: CreativesContainer channel prop (must match pane.key for tab to be active)
|
|
@@ -312,4 +232,4 @@ export const INCENTIVE_TYPES = [
|
|
|
312
232
|
{
|
|
313
233
|
value: 'badges', label: <FormattedMessage {...messages.badges} />, isActive: false, disabled: false,
|
|
314
234
|
},
|
|
315
|
-
];
|
|
235
|
+
];
|
|
@@ -9,6 +9,7 @@
|
|
|
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';
|
|
12
13
|
|
|
13
14
|
const CommunicationFlowContainer = ({
|
|
14
15
|
config,
|
|
@@ -17,16 +18,22 @@ const CommunicationFlowContainer = ({
|
|
|
17
18
|
onCancel,
|
|
18
19
|
onChange,
|
|
19
20
|
...otherProps
|
|
20
|
-
}) =>
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
}) => {
|
|
22
|
+
if (!hasSupportEngagementModule()) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<CommunicationFlow
|
|
28
|
+
config={config}
|
|
29
|
+
initialData={initialData}
|
|
30
|
+
onSave={onSave}
|
|
31
|
+
onCancel={onCancel}
|
|
32
|
+
onChange={onChange}
|
|
33
|
+
{...otherProps}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
30
37
|
|
|
31
38
|
CommunicationFlowContainer.propTypes = {
|
|
32
39
|
config: PropTypes.shape({
|
|
@@ -80,13 +87,11 @@ CommunicationFlowContainer.propTypes = {
|
|
|
80
87
|
controls: PropTypes.array,
|
|
81
88
|
}),
|
|
82
89
|
}),
|
|
83
|
-
context: PropTypes.object, // ouId, campaignId, programId
|
|
84
|
-
//
|
|
85
|
-
useCCS: PropTypes.bool, // If false, skips the CCS createCommDefinition call on save. Defaults to true.
|
|
90
|
+
context: PropTypes.object, // ouId, campaignId, programId, etc.
|
|
91
|
+
useCCS: PropTypes.bool, // If false, skips CCS bulk-claim-approve on save. Defaults to true.
|
|
86
92
|
}).isRequired,
|
|
87
93
|
initialData: PropTypes.object, // for edit/preview mode
|
|
88
|
-
onSave: PropTypes.func.isRequired, // (data) => void - called when user saves
|
|
89
|
-
// ({id, referenceId, version, status}) is present when the CCS create succeeded
|
|
94
|
+
onSave: PropTypes.func.isRequired, // (data) => void - called when user saves
|
|
90
95
|
onCancel: PropTypes.func.isRequired, // () => void - called when user cancels
|
|
91
96
|
onChange: PropTypes.func, // (data) => void - optional, called on data changes
|
|
92
97
|
};
|
|
@@ -363,8 +363,4 @@ 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
|
-
},
|
|
370
366
|
};
|
|
@@ -29,7 +29,6 @@ import {
|
|
|
29
29
|
getEmailDefaultsForDomainId,
|
|
30
30
|
getSmsDefaultsForDomainId,
|
|
31
31
|
getRcsAccountName,
|
|
32
|
-
getEmailDomainGatewayMapIdByDomainId,
|
|
33
32
|
} from './deliverySettingsConfig';
|
|
34
33
|
import messages from '../../messages';
|
|
35
34
|
import './SenderDetails.scss';
|
|
@@ -152,11 +151,6 @@ const SenderDetails = ({
|
|
|
152
151
|
saveValues.whatsappDomainId = matchedDomain?.domainProperties?.id ?? matchedDomain?.domainId ?? matchedDomain?.id;
|
|
153
152
|
}
|
|
154
153
|
}
|
|
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
|
-
}
|
|
160
154
|
onSave(saveValues);
|
|
161
155
|
}
|
|
162
156
|
onClose();
|
package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js
CHANGED
|
@@ -83,7 +83,6 @@ const ENTITIES = {
|
|
|
83
83
|
emailTwoDomains: {
|
|
84
84
|
EMAIL: [
|
|
85
85
|
{
|
|
86
|
-
id: 'gwmap-a',
|
|
87
86
|
domainProperties: {
|
|
88
87
|
id: 'em-a',
|
|
89
88
|
domainName: 'Mail Alpha',
|
|
@@ -102,7 +101,6 @@ const ENTITIES = {
|
|
|
102
101
|
},
|
|
103
102
|
},
|
|
104
103
|
{
|
|
105
|
-
id: 'gwmap-b',
|
|
106
104
|
domainProperties: {
|
|
107
105
|
id: 'em-b',
|
|
108
106
|
domainName: 'Mail Beta',
|
|
@@ -324,29 +322,6 @@ describe('SenderDetails', () => {
|
|
|
324
322
|
});
|
|
325
323
|
});
|
|
326
324
|
|
|
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
|
-
|
|
350
325
|
it('resets email domain row back to the first gateway', async () => {
|
|
351
326
|
renderSenderDetails({
|
|
352
327
|
channels: ['EMAIL'],
|
|
@@ -438,7 +438,6 @@ describe('deliverySettingsConfig — user-facing flows', () => {
|
|
|
438
438
|
smsDomain: 'd1',
|
|
439
439
|
smsSenderId: 'g1',
|
|
440
440
|
emailDomain: 'ed',
|
|
441
|
-
emailDomainGatewayMapId: 'gwmap-1',
|
|
442
441
|
emailSenderId: 'mail@x.com',
|
|
443
442
|
emailSenderName: 'N',
|
|
444
443
|
emailReplyToId: 'r@x.com',
|
|
@@ -456,10 +455,9 @@ describe('deliverySettingsConfig — user-facing flows', () => {
|
|
|
456
455
|
SMS: { domainId: 'd1', gsmSenderId: 'g1' },
|
|
457
456
|
EMAIL: {
|
|
458
457
|
domainId: 'ed',
|
|
459
|
-
|
|
460
|
-
senderId: 'mail@x.com',
|
|
458
|
+
senderEmail: 'mail@x.com',
|
|
461
459
|
senderLabel: 'N',
|
|
462
|
-
|
|
460
|
+
senderReplyTo: 'r@x.com',
|
|
463
461
|
},
|
|
464
462
|
WHATSAPP: { domainId: 'wd', senderMobNum: '+w' },
|
|
465
463
|
RCS: { senderMobNum: '+r', rcsSender: '+r' },
|
|
@@ -474,10 +472,9 @@ describe('deliverySettingsConfig — user-facing flows', () => {
|
|
|
474
472
|
SMS: { domainId: 'd', gsmSenderId: 'g' },
|
|
475
473
|
EMAIL: {
|
|
476
474
|
domainId: 'ed',
|
|
477
|
-
|
|
478
|
-
senderId: 'e@e.com',
|
|
475
|
+
senderEmail: 'e@e.com',
|
|
479
476
|
senderLabel: 'L',
|
|
480
|
-
|
|
477
|
+
senderReplyTo: 'r@e.com',
|
|
481
478
|
},
|
|
482
479
|
WHATSAPP: { senderMobNum: '+w', domainId: 'wid' },
|
|
483
480
|
RCS: { rcsSender: '+r' },
|
|
@@ -489,10 +486,6 @@ describe('deliverySettingsConfig — user-facing flows', () => {
|
|
|
489
486
|
smsDomain: 'd',
|
|
490
487
|
smsSenderId: 'g',
|
|
491
488
|
emailDomain: 'ed',
|
|
492
|
-
emailDomainGatewayMapId: 'gwmap-2',
|
|
493
|
-
emailSenderId: 'e@e.com',
|
|
494
|
-
emailSenderName: 'L',
|
|
495
|
-
emailReplyToId: 'r@e.com',
|
|
496
489
|
whatsappDomainId: 'wid',
|
|
497
490
|
viberSenderId: 'vx',
|
|
498
491
|
});
|
|
@@ -181,14 +181,6 @@ 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
|
-
|
|
192
184
|
/** Domain name for summary display (parseEntityForDisplay) */
|
|
193
185
|
const getEmailDomainNameForDisplay = (entity) => {
|
|
194
186
|
const emailData = getEmailData(entity);
|
|
@@ -270,8 +262,6 @@ const getEmailDefaultsForDomain = (domainData) => {
|
|
|
270
262
|
* context.fieldValues has current form state (e.g. selected emailDomain)
|
|
271
263
|
* Aligned with adiona-ui/cap-campaigns-v2: default = contactInfo item with default:true, or first
|
|
272
264
|
*/
|
|
273
|
-
export { getEmailDomainGatewayMapIdByDomainId };
|
|
274
|
-
|
|
275
265
|
export const getDefaultValueForField = (fieldKey, entity, context = {}) => {
|
|
276
266
|
if (!entity) return '';
|
|
277
267
|
const fieldValues = context?.fieldValues || {};
|
|
@@ -584,11 +574,7 @@ export const parseEntityForDisplay = (entity, context = {}) => {
|
|
|
584
574
|
senderDetails: entity,
|
|
585
575
|
smsSenderId: getSmsSenderIdValue(entity),
|
|
586
576
|
smsDomain: getSmsDomainValue(entity),
|
|
587
|
-
// emailDomain here is the domain NAME (for display only) - buildChannelSettingFromFieldValues
|
|
588
|
-
// needs the numeric domainId/domainGatewayMapId, sourced separately below.
|
|
589
577
|
emailDomain: getEmailDomainNameForDisplay(entity),
|
|
590
|
-
emailDomainId: getEmailDomainValue(entity),
|
|
591
|
-
emailDomainGatewayMapId: getEmailDomainGatewayMapIdByDomainId(entity, getEmailDomainValue(entity)),
|
|
592
578
|
emailSenderId: getEmailSenderIdValue(entity),
|
|
593
579
|
viberSenderId: getSenderIdFromContactInfo(entity, 'VIBER'),
|
|
594
580
|
zaloSenderId: getSenderIdFromContactInfo(entity, 'ZALO'),
|
|
@@ -614,14 +600,10 @@ export const buildChannelSettingFromFieldValues = (fieldValues = {}) => {
|
|
|
614
600
|
}
|
|
615
601
|
if (fieldValues.emailDomain != null || fieldValues.emailSenderId != null || fieldValues.emailSenderName != null || fieldValues.emailReplyToId != null) {
|
|
616
602
|
channelSetting.EMAIL = {
|
|
617
|
-
|
|
618
|
-
|
|
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,
|
|
603
|
+
domainId: fieldValues.emailDomain,
|
|
604
|
+
senderEmail: fieldValues.emailSenderId,
|
|
623
605
|
senderLabel: fieldValues.emailSenderName,
|
|
624
|
-
|
|
606
|
+
senderReplyTo: fieldValues.emailReplyToId,
|
|
625
607
|
};
|
|
626
608
|
}
|
|
627
609
|
if (fieldValues.whatsappSenderNumber != null || fieldValues.whatsappAccount != null) {
|
|
@@ -691,10 +673,9 @@ export const parseChannelSettingForDisplay = (channelSetting = {}) => {
|
|
|
691
673
|
}
|
|
692
674
|
if (channelSetting.EMAIL) {
|
|
693
675
|
out.emailDomain = channelSetting.EMAIL.domainId;
|
|
694
|
-
out.
|
|
695
|
-
out.emailSenderId = channelSetting.EMAIL.senderId;
|
|
676
|
+
out.emailSenderId = channelSetting.EMAIL.senderEmail;
|
|
696
677
|
out.emailSenderName = channelSetting.EMAIL.senderLabel;
|
|
697
|
-
out.emailReplyToId = channelSetting.EMAIL.
|
|
678
|
+
out.emailReplyToId = channelSetting.EMAIL.senderReplyTo;
|
|
698
679
|
}
|
|
699
680
|
if (channelSetting.WHATSAPP) {
|
|
700
681
|
out.whatsappSenderNumber = channelSetting.WHATSAPP.senderMobNum;
|
|
@@ -1139,8 +1139,7 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
|
|
|
1139
1139
|
this.props.globalActions.addMessageToQueue(message);
|
|
1140
1140
|
} else {
|
|
1141
1141
|
const name = data.file.name.split('.');
|
|
1142
|
-
const
|
|
1143
|
-
const newFile = new File([blob], `${name[0]}${Date.now()}.${name[1]}`, {type: 'image'});
|
|
1142
|
+
const newFile = new File([data.file], `${name[0]}${Date.now()}.${name[1]}`, {type: 'image'});
|
|
1144
1143
|
this.props.actions.uploadAsset(newFile, data.type, data.fileParams);
|
|
1145
1144
|
}
|
|
1146
1145
|
};
|