@capillarytech/creatives-library 9.0.49-alpha.1 → 9.0.49
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/package.json +1 -1
- package/services/api.js +0 -9
- package/v2Containers/CommunicationFlow/CommunicationFlow.js +58 -85
- package/v2Containers/CommunicationFlow/CommunicationFlowCard.js +4 -20
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +51 -146
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlowCard.test.js +0 -16
- package/v2Containers/CommunicationFlow/constants.js +0 -40
- package/v2Containers/CommunicationFlow/index.js +3 -5
- package/v2Containers/CommunicationFlow/messages.js +0 -4
- package/v2Containers/Templates/index.js +15 -60
- package/v2Containers/Templates/messages.js +0 -4
- package/v2Containers/Whatsapp/index.js +1 -1
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));
|
|
@@ -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,9 +35,9 @@ import {
|
|
|
36
35
|
CHANNELS,
|
|
37
36
|
INCENTIVE_TYPES,
|
|
38
37
|
DYNAMIC_CONTROLS_CONFIG,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
CHANNEL_CONTENT_KEY_MAP,
|
|
39
|
+
CHANNEL_DELIVERY_KEY_MAP,
|
|
40
|
+
CAMPAIGNS,
|
|
42
41
|
} from './constants';
|
|
43
42
|
import { getEnabledSteps } from './utils/getEnabledSteps';
|
|
44
43
|
import messages from './messages';
|
|
@@ -65,25 +64,6 @@ const hasDeliverySettingForChannel = (channelSetting, channel) => {
|
|
|
65
64
|
return !!settings && Object.values(settings).some((v) => v !== null && v !== '' && v !== undefined);
|
|
66
65
|
};
|
|
67
66
|
|
|
68
|
-
// createCommDefinition resolves (rather than rejects) with the CCS error envelope
|
|
69
|
-
// for 4xx/5xx responses — see api.js's request()/checkStatus. A duplicate
|
|
70
|
-
// referenceId within the org comes back as this specific 409.
|
|
71
|
-
const isDuplicateReferenceIdError = (res) => res?.success === false && res?.status?.message === 'REFERENCE_ID_EXISTS';
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* CCS requires referenceId on create; the consumer-supplied Alert/comm name is
|
|
75
|
-
* mandatory but referenceId is optional in every consumer's own form. Generate
|
|
76
|
-
* a stable fallback from the name rather than failing the save.
|
|
77
|
-
*/
|
|
78
|
-
const buildCcsReferenceId = (name) => {
|
|
79
|
-
const slug = (name || 'COMM')
|
|
80
|
-
.trim()
|
|
81
|
-
.toUpperCase()
|
|
82
|
-
.replace(/[^A-Z0-9]+/g, '_')
|
|
83
|
-
.replace(/^_+|_+$/g, '') || 'COMM';
|
|
84
|
-
return `${slug}_${Date.now()}`;
|
|
85
|
-
};
|
|
86
|
-
|
|
87
67
|
const CommunicationFlow = ({
|
|
88
68
|
config,
|
|
89
69
|
initialData,
|
|
@@ -111,7 +91,6 @@ const CommunicationFlow = ({
|
|
|
111
91
|
};
|
|
112
92
|
});
|
|
113
93
|
const [validationErrors, setValidationErrors] = useState({});
|
|
114
|
-
const [saveError, setSaveError] = useState(null);
|
|
115
94
|
|
|
116
95
|
// Memoize enabled steps
|
|
117
96
|
const enabledSteps = useMemo(() => getEnabledSteps(config), [config]);
|
|
@@ -171,75 +150,74 @@ const CommunicationFlow = ({
|
|
|
171
150
|
}, []);
|
|
172
151
|
|
|
173
152
|
const handleSave = useCallback(async () => {
|
|
174
|
-
setSaveError(null);
|
|
175
153
|
const aggregatedData = getAggregatedData();
|
|
176
154
|
const shouldUseCCS = config?.useCCS !== false;
|
|
177
|
-
let ccsCommDefinition = null;
|
|
178
155
|
|
|
179
156
|
if (shouldUseCCS) {
|
|
180
|
-
const
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
// has no name input of its own, so this is mandatory input from config.context.
|
|
184
|
-
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);
|
|
185
160
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
if (!isMultiChannel && contentItem && name) {
|
|
189
|
-
const channel = (contentItem.channel || '').toUpperCase();
|
|
190
|
-
const contentKey = CCS_CHANNEL_CONTENT_KEY_MAP[channel];
|
|
191
|
-
const deliveryKey = CCS_CHANNEL_DELIVERY_KEY_MAP[channel];
|
|
192
|
-
const { dynamicControls = {} } = aggregatedData;
|
|
193
|
-
const channelSettings = aggregatedData.deliverySetting?.channelSetting?.[channel] || {};
|
|
194
|
-
const referenceId = config?.context?.referenceId || buildCcsReferenceId(name);
|
|
161
|
+
const channelContentKeyMap = CHANNEL_CONTENT_KEY_MAP;
|
|
162
|
+
const channelDeliveryKeyMap = CHANNEL_DELIVERY_KEY_MAP;
|
|
195
163
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
name,
|
|
199
|
-
description: config?.context?.description || undefined,
|
|
200
|
-
strategyType: CCS_STRATEGY_TYPE_SINGLE,
|
|
201
|
-
settings: {
|
|
202
|
-
additionalSettings: {
|
|
203
|
-
useTinyUrl: dynamicControls.useTinyUrl ?? false,
|
|
204
|
-
encryptUrl: dynamicControls.sendToControlCustomers ?? false,
|
|
205
|
-
linkTrackingEnabled: dynamicControls.overrideDailyLimit ?? false,
|
|
206
|
-
userSubscriptionDisabled: dynamicControls.sendToBrandPocs ?? false,
|
|
207
|
-
},
|
|
208
|
-
executionParams: {},
|
|
209
|
-
},
|
|
210
|
-
singleChannelStrategy: {
|
|
211
|
-
channel,
|
|
212
|
-
...(contentKey && { [contentKey]: contentItem.templateData }),
|
|
213
|
-
...(deliveryKey && { [deliveryKey]: { channelSettings: { channel, ...channelSettings } } }),
|
|
214
|
-
},
|
|
215
|
-
};
|
|
164
|
+
const contentItems = aggregatedData.contentItems || [];
|
|
165
|
+
const { dynamicControls = {} } = aggregatedData;
|
|
216
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) {
|
|
217
175
|
try {
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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);
|
|
233
213
|
}
|
|
234
214
|
} catch (error) {
|
|
235
|
-
console.error('[CommunicationFlow] CCS
|
|
215
|
+
console.error('[CommunicationFlow] CCS createCentralCommsMetaId error:', error);
|
|
236
216
|
}
|
|
237
|
-
} else if (!name) {
|
|
238
|
-
console.warn('[CommunicationFlow] Skipping CCS createCommDefinition — config.context.name is required');
|
|
239
217
|
}
|
|
240
218
|
}
|
|
241
219
|
|
|
242
|
-
onSave(
|
|
220
|
+
onSave(aggregatedData);
|
|
243
221
|
}, [getAggregatedData, config, onSave]);
|
|
244
222
|
|
|
245
223
|
// Call onChange callback when stepData changes
|
|
@@ -339,11 +317,6 @@ const CommunicationFlow = ({
|
|
|
339
317
|
{renderSteps()}
|
|
340
318
|
{onSave && (
|
|
341
319
|
<CapRow useLegacy className="communication-flow-container__footer">
|
|
342
|
-
{saveError && (
|
|
343
|
-
<CapLabel type="label2" className="communication-flow-container__save-error">
|
|
344
|
-
{saveError}
|
|
345
|
-
</CapLabel>
|
|
346
|
-
)}
|
|
347
320
|
<CapButton type="primary" onClick={handleSave} disabled={isSaveDisabled}>
|
|
348
321
|
{formatMessage(messages.save)}
|
|
349
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 {
|
|
@@ -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,93 +359,74 @@ 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
|
-
channel: 'SMS',
|
|
393
|
-
smsMessageContent: { message: 'Hello' },
|
|
394
|
-
}),
|
|
386
|
+
centralCommsPayload: expect.objectContaining({ channel: 'SMS', module: 'CAMPAIGNS' }),
|
|
395
387
|
}),
|
|
396
388
|
);
|
|
397
389
|
expect(onSave).toHaveBeenCalledTimes(1);
|
|
398
390
|
});
|
|
399
391
|
|
|
400
|
-
it('
|
|
401
|
-
const onSave = jest.fn();
|
|
392
|
+
it('calls getCentralCommsMetaIds when metaIds are returned from createCentralCommsMetaId', async () => {
|
|
402
393
|
renderWithFlow({
|
|
403
394
|
features: {},
|
|
404
|
-
|
|
405
|
-
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
406
|
-
onSave,
|
|
395
|
+
initialData: { contentItems: [{ channel: 'EMAIL', templateData: {} }] },
|
|
407
396
|
});
|
|
408
397
|
|
|
409
398
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
410
399
|
|
|
411
|
-
await waitFor(() => expect(
|
|
412
|
-
expect(onSave).toHaveBeenCalledWith(
|
|
413
|
-
expect.objectContaining({
|
|
414
|
-
ccsCommDefinition: { id: 'cd_123', referenceId: 'ORDER_PLACED_123', version: 1, status: 'DRAFT' },
|
|
415
|
-
}),
|
|
416
|
-
);
|
|
400
|
+
await waitFor(() => expect(getCentralCommsMetaIds).toHaveBeenCalledWith('meta-123'));
|
|
417
401
|
});
|
|
418
402
|
|
|
419
|
-
it('
|
|
420
|
-
|
|
421
|
-
// clears that return value before every test — set it explicitly here.
|
|
422
|
-
jest.spyOn(Date, 'now').mockReturnValue(1612267539410);
|
|
403
|
+
it('skips getCentralCommsMetaIds when response contains no id', async () => {
|
|
404
|
+
createCentralCommsMetaId.mockResolvedValue({ response: { data: {} } });
|
|
423
405
|
renderWithFlow({
|
|
424
406
|
features: {},
|
|
425
|
-
config: ccsConfig,
|
|
426
407
|
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
427
408
|
});
|
|
428
409
|
|
|
429
410
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
430
411
|
|
|
431
|
-
await waitFor(() => expect(
|
|
432
|
-
|
|
433
|
-
expect(payload.referenceId).toBe('ORDER_PLACED_NOTIFICATION_1612267539410');
|
|
412
|
+
await waitFor(() => expect(createCentralCommsMetaId).toHaveBeenCalled());
|
|
413
|
+
expect(getCentralCommsMetaIds).not.toHaveBeenCalled();
|
|
434
414
|
});
|
|
435
415
|
|
|
436
|
-
it('uses config.context
|
|
416
|
+
it('uses ouId and module from config.context when provided', async () => {
|
|
437
417
|
renderWithFlow({
|
|
438
418
|
features: {},
|
|
439
|
-
config: { ...
|
|
419
|
+
config: { ...baseConfig, context: { ouId: 42, module: 'LOYALTY' }, features: {} },
|
|
440
420
|
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
441
421
|
});
|
|
442
422
|
|
|
443
423
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
444
424
|
|
|
445
|
-
await waitFor(() => expect(
|
|
446
|
-
expect(
|
|
447
|
-
expect.objectContaining({
|
|
425
|
+
await waitFor(() => expect(createCentralCommsMetaId).toHaveBeenCalled());
|
|
426
|
+
expect(createCentralCommsMetaId).toHaveBeenCalledWith(
|
|
427
|
+
expect.objectContaining({
|
|
428
|
+
centralCommsPayload: expect.objectContaining({ ouId: 42, module: 'LOYALTY' }),
|
|
429
|
+
}),
|
|
448
430
|
);
|
|
449
431
|
});
|
|
450
432
|
|
|
@@ -452,7 +434,7 @@ describe('handleSave — CCS flow', () => {
|
|
|
452
434
|
const onSave = jest.fn();
|
|
453
435
|
renderWithFlow({
|
|
454
436
|
features: {},
|
|
455
|
-
config: { ...
|
|
437
|
+
config: { ...baseConfig, useCCS: false, features: {} },
|
|
456
438
|
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
457
439
|
onSave,
|
|
458
440
|
});
|
|
@@ -460,50 +442,14 @@ describe('handleSave — CCS flow', () => {
|
|
|
460
442
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
461
443
|
|
|
462
444
|
await waitFor(() => expect(onSave).toHaveBeenCalledTimes(1));
|
|
463
|
-
expect(
|
|
464
|
-
expect(onSave).toHaveBeenCalledWith(expect.not.objectContaining({ ccsCommDefinition: expect.anything() }));
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
it('skips createCommDefinition when config.context.name is absent (mandatory)', async () => {
|
|
468
|
-
const onSave = jest.fn();
|
|
469
|
-
renderWithFlow({
|
|
470
|
-
features: {},
|
|
471
|
-
config: { ...baseConfig, features: {} },
|
|
472
|
-
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
473
|
-
onSave,
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
477
|
-
|
|
478
|
-
await waitFor(() => expect(onSave).toHaveBeenCalledTimes(1));
|
|
479
|
-
expect(createCommDefinition).not.toHaveBeenCalled();
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
it('skips createCommDefinition for multi-channel strategies (CHANNEL_PRIORITY/AB_TEST)', async () => {
|
|
483
|
-
const onSave = jest.fn();
|
|
484
|
-
renderWithFlow({
|
|
485
|
-
features: {},
|
|
486
|
-
config: ccsConfig,
|
|
487
|
-
initialData: {
|
|
488
|
-
communicationStrategy: CHANNEL_PRIORITY,
|
|
489
|
-
channels: ['SMS', 'EMAIL'],
|
|
490
|
-
contentItems: [{ channel: 'SMS', templateData: {} }, { channel: 'EMAIL', templateData: {} }],
|
|
491
|
-
},
|
|
492
|
-
onSave,
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
496
|
-
|
|
497
|
-
await waitFor(() => expect(onSave).toHaveBeenCalledTimes(1));
|
|
498
|
-
expect(createCommDefinition).not.toHaveBeenCalled();
|
|
445
|
+
expect(createCentralCommsMetaId).not.toHaveBeenCalled();
|
|
499
446
|
});
|
|
500
447
|
|
|
501
|
-
it('still calls onSave
|
|
502
|
-
|
|
448
|
+
it('still calls onSave when createCentralCommsMetaId rejects', async () => {
|
|
449
|
+
createCentralCommsMetaId.mockRejectedValue(new Error('Network error'));
|
|
503
450
|
const onSave = jest.fn();
|
|
504
451
|
renderWithFlow({
|
|
505
452
|
features: {},
|
|
506
|
-
config: ccsConfig,
|
|
507
453
|
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
508
454
|
onSave,
|
|
509
455
|
});
|
|
@@ -511,35 +457,12 @@ describe('handleSave — CCS flow', () => {
|
|
|
511
457
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
512
458
|
|
|
513
459
|
await waitFor(() => expect(onSave).toHaveBeenCalledTimes(1));
|
|
514
|
-
expect(onSave).toHaveBeenCalledWith(expect.not.objectContaining({ ccsCommDefinition: expect.anything() }));
|
|
515
460
|
});
|
|
516
461
|
|
|
517
|
-
it('
|
|
518
|
-
createCommDefinition.mockResolvedValue({
|
|
519
|
-
success: false,
|
|
520
|
-
status: { isError: true, code: 409, message: 'REFERENCE_ID_EXISTS' },
|
|
521
|
-
message: 'REFERENCE_ID_EXISTS',
|
|
522
|
-
});
|
|
462
|
+
it('skips createCentralCommsMetaId when contentItems is empty', async () => {
|
|
523
463
|
const onSave = jest.fn();
|
|
524
464
|
renderWithFlow({
|
|
525
465
|
features: {},
|
|
526
|
-
config: ccsConfig,
|
|
527
|
-
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
528
|
-
onSave,
|
|
529
|
-
});
|
|
530
|
-
|
|
531
|
-
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
532
|
-
|
|
533
|
-
await waitFor(() => expect(createCommDefinition).toHaveBeenCalledTimes(1));
|
|
534
|
-
expect(await screen.findByText(/already in use in your organization/i)).toBeInTheDocument();
|
|
535
|
-
expect(onSave).not.toHaveBeenCalled();
|
|
536
|
-
});
|
|
537
|
-
|
|
538
|
-
it('skips createCommDefinition when contentItems is empty', async () => {
|
|
539
|
-
const onSave = jest.fn();
|
|
540
|
-
renderWithFlow({
|
|
541
|
-
features: {},
|
|
542
|
-
config: ccsConfig,
|
|
543
466
|
initialData: { contentItems: [] },
|
|
544
467
|
onSave,
|
|
545
468
|
});
|
|
@@ -547,13 +470,12 @@ describe('handleSave — CCS flow', () => {
|
|
|
547
470
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
548
471
|
|
|
549
472
|
await waitFor(() => expect(onSave).toHaveBeenCalledTimes(1));
|
|
550
|
-
expect(
|
|
473
|
+
expect(createCentralCommsMetaId).not.toHaveBeenCalled();
|
|
551
474
|
});
|
|
552
475
|
|
|
553
|
-
it('
|
|
476
|
+
it('includes additionalSettings derived from dynamicControls in the payload', async () => {
|
|
554
477
|
renderWithFlow({
|
|
555
478
|
features: {},
|
|
556
|
-
config: ccsConfig,
|
|
557
479
|
initialData: {
|
|
558
480
|
contentItems: [{ channel: 'SMS', templateData: {} }],
|
|
559
481
|
dynamicControls: {
|
|
@@ -567,36 +489,14 @@ describe('handleSave — CCS flow', () => {
|
|
|
567
489
|
|
|
568
490
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
569
491
|
|
|
570
|
-
await waitFor(() => expect(
|
|
571
|
-
const payload =
|
|
572
|
-
expect(payload.
|
|
492
|
+
await waitFor(() => expect(createCentralCommsMetaId).toHaveBeenCalled());
|
|
493
|
+
const payload = createCentralCommsMetaId.mock.calls[0][0];
|
|
494
|
+
expect(payload.centralCommsPayload.smsDeliverySettings.additionalSettings).toEqual({
|
|
573
495
|
useTinyUrl: true,
|
|
574
496
|
encryptUrl: true,
|
|
575
497
|
linkTrackingEnabled: true,
|
|
576
498
|
userSubscriptionDisabled: true,
|
|
577
499
|
});
|
|
578
|
-
expect(payload.singleChannelStrategy.smsDeliverySettings.additionalSettings).toBeUndefined();
|
|
579
|
-
});
|
|
580
|
-
|
|
581
|
-
it('includes channelSettings from deliverySetting.channelSetting in the payload', async () => {
|
|
582
|
-
renderWithFlow({
|
|
583
|
-
features: {},
|
|
584
|
-
config: ccsConfig,
|
|
585
|
-
initialData: {
|
|
586
|
-
contentItems: [{ channel: 'SMS', templateData: {} }],
|
|
587
|
-
deliverySetting: { channelSetting: { SMS: { domainId: 1001, gsmSenderId: 'BRAND1' } } },
|
|
588
|
-
},
|
|
589
|
-
});
|
|
590
|
-
|
|
591
|
-
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
592
|
-
|
|
593
|
-
await waitFor(() => expect(createCommDefinition).toHaveBeenCalled());
|
|
594
|
-
const payload = createCommDefinition.mock.calls[0][0];
|
|
595
|
-
expect(payload.singleChannelStrategy.smsDeliverySettings.channelSettings).toEqual({
|
|
596
|
-
channel: 'SMS',
|
|
597
|
-
domainId: 1001,
|
|
598
|
-
gsmSenderId: 'BRAND1',
|
|
599
|
-
});
|
|
600
500
|
});
|
|
601
501
|
});
|
|
602
502
|
|
|
@@ -652,34 +552,39 @@ describe('optional chaining safety', () => {
|
|
|
652
552
|
jest.clearAllMocks();
|
|
653
553
|
});
|
|
654
554
|
|
|
655
|
-
it('
|
|
656
|
-
|
|
555
|
+
it('defaults ouId to -1 when config.context is absent', async () => {
|
|
556
|
+
createCentralCommsMetaId.mockResolvedValue({ response: { data: { id: 'x' } } });
|
|
657
557
|
renderWithFlow({
|
|
658
558
|
features: {},
|
|
659
|
-
config: { ...baseConfig, features: {} },
|
|
660
559
|
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
661
|
-
onSave,
|
|
662
560
|
});
|
|
663
561
|
|
|
664
562
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
665
563
|
|
|
666
|
-
await waitFor(() => expect(
|
|
667
|
-
expect(
|
|
564
|
+
await waitFor(() => expect(createCentralCommsMetaId).toHaveBeenCalled());
|
|
565
|
+
expect(createCentralCommsMetaId).toHaveBeenCalledWith(
|
|
566
|
+
expect.objectContaining({
|
|
567
|
+
centralCommsPayload: expect.objectContaining({ ouId: -1 }),
|
|
568
|
+
}),
|
|
569
|
+
);
|
|
668
570
|
});
|
|
669
571
|
|
|
670
|
-
it('
|
|
671
|
-
|
|
572
|
+
it('defaults module to consumer.toUpperCase() when config.context.module absent', async () => {
|
|
573
|
+
createCentralCommsMetaId.mockResolvedValue({ response: { data: { id: 'x' } } });
|
|
672
574
|
renderWithFlow({
|
|
673
575
|
features: {},
|
|
674
|
-
config: { ...baseConfig,
|
|
576
|
+
config: { ...baseConfig, consumer: 'loyalty', features: {} },
|
|
675
577
|
initialData: { contentItems: [{ channel: 'SMS', templateData: {} }] },
|
|
676
|
-
onSave,
|
|
677
578
|
});
|
|
678
579
|
|
|
679
580
|
await userEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
680
581
|
|
|
681
|
-
await waitFor(() => expect(
|
|
682
|
-
expect(
|
|
582
|
+
await waitFor(() => expect(createCentralCommsMetaId).toHaveBeenCalled());
|
|
583
|
+
expect(createCentralCommsMetaId).toHaveBeenCalledWith(
|
|
584
|
+
expect.objectContaining({
|
|
585
|
+
centralCommsPayload: expect.objectContaining({ module: 'LOYALTY' }),
|
|
586
|
+
}),
|
|
587
|
+
);
|
|
683
588
|
});
|
|
684
589
|
|
|
685
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)', () => {
|
|
@@ -38,46 +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, "Channel Strategy overview" / class dump). Kept separate from
|
|
48
|
-
// CHANNEL_CONTENT_KEY_MAP/CHANNEL_DELIVERY_KEY_MAP below, which are the legacy
|
|
49
|
-
// messageMeta payload's keys and do not match CCS naming for every channel
|
|
50
|
-
// (e.g. legacy uses lowercase 'mpushMessageContent'; CCS uses 'mPushMessageContent').
|
|
51
|
-
export const CCS_CHANNEL_CONTENT_KEY_MAP = {
|
|
52
|
-
SMS: 'smsMessageContent',
|
|
53
|
-
EMAIL: 'emailMessageContent',
|
|
54
|
-
WHATSAPP: 'whatsappMessageContent',
|
|
55
|
-
MPUSH: 'mPushMessageContent',
|
|
56
|
-
INAPP: 'inAppMessageContent',
|
|
57
|
-
ANDROID: 'androidMessageContent',
|
|
58
|
-
IOS: 'iosMessageContent',
|
|
59
|
-
ZALO: 'zaloMessageContent',
|
|
60
|
-
VIBER: 'viberMessageContent',
|
|
61
|
-
LINE: 'lineMessageContent',
|
|
62
|
-
WEBPUSH: 'webPushMessageContent',
|
|
63
|
-
RCS: 'rcsMessageContent',
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
export const CCS_CHANNEL_DELIVERY_KEY_MAP = {
|
|
67
|
-
SMS: 'smsDeliverySettings',
|
|
68
|
-
EMAIL: 'emailDeliverySettings',
|
|
69
|
-
WHATSAPP: 'whatsappDeliverySettings',
|
|
70
|
-
MPUSH: 'mPushDeliverySettings',
|
|
71
|
-
INAPP: 'inAppDeliverySettings',
|
|
72
|
-
ANDROID: 'androidDeliverySettings',
|
|
73
|
-
IOS: 'iosDeliverySettings',
|
|
74
|
-
ZALO: 'zaloDeliverySettings',
|
|
75
|
-
VIBER: 'viberDeliverySettings',
|
|
76
|
-
LINE: 'lineDeliverySettings',
|
|
77
|
-
WEBPUSH: 'webPushDeliverySettings',
|
|
78
|
-
RCS: 'rcsDeliverySettings',
|
|
79
|
-
};
|
|
80
|
-
|
|
81
41
|
// Channel config - single source of truth for CreativesContainer and TemplatesV2
|
|
82
42
|
// paneKey: TemplatesV2 defaultPanes object key (for channelsToHide)
|
|
83
43
|
// channelProp: CreativesContainer channel prop (must match pane.key for tab to be active)
|
|
@@ -87,13 +87,11 @@ CommunicationFlowContainer.propTypes = {
|
|
|
87
87
|
controls: PropTypes.array,
|
|
88
88
|
}),
|
|
89
89
|
}),
|
|
90
|
-
context: PropTypes.object, // ouId, campaignId, programId
|
|
91
|
-
//
|
|
92
|
-
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.
|
|
93
92
|
}).isRequired,
|
|
94
93
|
initialData: PropTypes.object, // for edit/preview mode
|
|
95
|
-
onSave: PropTypes.func.isRequired, // (data) => void - called when user saves
|
|
96
|
-
// ({id, referenceId, version, status}) is present when the CCS create succeeded
|
|
94
|
+
onSave: PropTypes.func.isRequired, // (data) => void - called when user saves
|
|
97
95
|
onCancel: PropTypes.func.isRequired, // () => void - called when user cancels
|
|
98
96
|
onChange: PropTypes.func, // (data) => void - optional, called on data changes
|
|
99
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
|
};
|
|
@@ -2051,21 +2051,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2051
2051
|
const isCardArchiveEligible = isArchivalEnabled && this.isChannelArchiveEligible(currentChannel, cardWhatsappStatus, cardRcsStatus);
|
|
2052
2052
|
const isArchivedMode = isArchivalEnabled && get(this.props, 'Templates.isArchivedMode', false);
|
|
2053
2053
|
const isAnyArchiveInProgress = isArchivalEnabled && !!(get(this.props, 'Templates.archiveInProgress') || get(this.props, 'Templates.unarchiveInProgress') || get(this.props, 'Templates.bulkArchiveInProgress') || get(this.props, 'Templates.bulkUnarchiveInProgress'));
|
|
2054
|
-
const smsBaseForLegacyCheck = template?.versions?.base || {};
|
|
2055
|
-
const updatedSmsEditorForLegacy = smsBaseForLegacyCheck['updated-sms-editor'];
|
|
2056
|
-
const updatedSmsEditorForLegacyJoined = Array.isArray(updatedSmsEditorForLegacy)
|
|
2057
|
-
? updatedSmsEditorForLegacy.join('')
|
|
2058
|
-
: updatedSmsEditorForLegacy;
|
|
2059
|
-
const smsBodyForLegacyCheck = `${updatedSmsEditorForLegacyJoined || ''}${smsBaseForLegacyCheck['sms-editor'] || ''}`;
|
|
2060
|
-
const isDltLegacyTemplate = currentChannel === SMS
|
|
2061
|
-
&& isTraiDltFeature
|
|
2062
|
-
&& !this.props.isFullMode
|
|
2063
|
-
&& DLT_LEGACY_VAR_REGEX.test(smsBodyForLegacyCheck);
|
|
2064
2054
|
const templateData = {
|
|
2065
2055
|
key: `${currentChannel}-card-${template?.name}`,
|
|
2066
2056
|
title: (
|
|
2067
2057
|
<span className="template-card-title" title={template?.name}>
|
|
2068
|
-
{isCardArchiveEligible && this.renderCardSelectionCheckbox({ templateId: template._id, selectedIds: selectedIdsArrayForCard, isDisabled: isAnyArchiveInProgress
|
|
2058
|
+
{isCardArchiveEligible && this.renderCardSelectionCheckbox({ templateId: template._id, selectedIds: selectedIdsArrayForCard, isDisabled: isAnyArchiveInProgress })}
|
|
2069
2059
|
<CapLabel.CapLabelInline type="label1" title={template?.name} className="template-card-name">
|
|
2070
2060
|
{template?.name}
|
|
2071
2061
|
{currentChannel === INAPP && (
|
|
@@ -2125,39 +2115,20 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2125
2115
|
})()
|
|
2126
2116
|
],
|
|
2127
2117
|
hoverOption: isArchivedMode || !this.canPerform(PERMISSIONS.CREATIVE_EDIT) ? null : (
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
</CapButton>
|
|
2143
|
-
</CapLabel.CapLabelInline>
|
|
2144
|
-
</CapTooltip>
|
|
2145
|
-
) : (
|
|
2146
|
-
<CapButton
|
|
2147
|
-
className={
|
|
2148
|
-
this.props.isFullMode
|
|
2149
|
-
? `edit-${channelLowerCase}`
|
|
2150
|
-
: `select-${channelLowerCase}`
|
|
2151
|
-
}
|
|
2152
|
-
onClick={e =>
|
|
2153
|
-
handlers.handleEditClick(e, template, undefined, undefined, {
|
|
2154
|
-
account: this.state.selectedAccount
|
|
2155
|
-
})
|
|
2156
|
-
}
|
|
2157
|
-
>
|
|
2158
|
-
{hoverButtonText}
|
|
2159
|
-
</CapButton>
|
|
2160
|
-
)
|
|
2118
|
+
<CapButton
|
|
2119
|
+
className={
|
|
2120
|
+
this.props.isFullMode
|
|
2121
|
+
? `edit-${channelLowerCase}`
|
|
2122
|
+
: `select-${channelLowerCase}`
|
|
2123
|
+
}
|
|
2124
|
+
onClick={e =>
|
|
2125
|
+
handlers.handleEditClick(e, template, undefined, undefined, {
|
|
2126
|
+
account: this.state.selectedAccount
|
|
2127
|
+
})
|
|
2128
|
+
}
|
|
2129
|
+
>
|
|
2130
|
+
{hoverButtonText}
|
|
2131
|
+
</CapButton>
|
|
2161
2132
|
)
|
|
2162
2133
|
};
|
|
2163
2134
|
const {
|
|
@@ -3603,22 +3574,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
3603
3574
|
CapNotification.error({ message: this.props.intl.formatMessage(messages.cannotEditArchivedTemplate) });
|
|
3604
3575
|
return;
|
|
3605
3576
|
}
|
|
3606
|
-
if (!this.props.isFullMode
|
|
3607
|
-
&& this.checkDLTfeatureEnable()
|
|
3608
|
-
&& (this.state.channel || '').toLowerCase() === SMS_LOWERCASE) {
|
|
3609
|
-
const smsBase = template?.versions?.base || {};
|
|
3610
|
-
const updatedSms = smsBase['updated-sms-editor'];
|
|
3611
|
-
const updatedSmsJoined = Array.isArray(updatedSms) ? updatedSms.join('') : updatedSms;
|
|
3612
|
-
// Check BOTH candidate fields — some legacy templates keep the body only in `sms-editor`
|
|
3613
|
-
// while others have it in `updated-sms-editor`. Concatenating catches either.
|
|
3614
|
-
const smsBody = `${updatedSmsJoined || ''}${smsBase['sms-editor'] || ''}`;
|
|
3615
|
-
if (DLT_LEGACY_VAR_REGEX.test(smsBody)) {
|
|
3616
|
-
CapNotification.error({
|
|
3617
|
-
message: this.props.intl.formatMessage(messages.smsLegacyBlockedTooltip),
|
|
3618
|
-
});
|
|
3619
|
-
return;
|
|
3620
|
-
}
|
|
3621
|
-
}
|
|
3622
3577
|
if (modeType && modeType !== undefined) {
|
|
3623
3578
|
this.setState({modeType});
|
|
3624
3579
|
}
|
|
@@ -546,10 +546,6 @@ export default defineMessages({
|
|
|
546
546
|
id: `${scope}.smsLegacyFormatBadge`,
|
|
547
547
|
defaultMessage: 'Legacy format',
|
|
548
548
|
},
|
|
549
|
-
"smsLegacyBlockedTooltip": {
|
|
550
|
-
id: `${scope}.smsLegacyBlockedTooltip`,
|
|
551
|
-
defaultMessage: "This template can't be used until re-registered with typed variables on the DLT portal.",
|
|
552
|
-
},
|
|
553
549
|
"uploadTemplate": {
|
|
554
550
|
id: `${scope}.uploadTemplate`,
|
|
555
551
|
defaultMessage: 'Upload template',
|
|
@@ -3161,7 +3161,7 @@ const isAuthenticationTemplate = isEqual(templateCategory, WHATSAPP_CATEGORIES.a
|
|
|
3161
3161
|
: templateMsgText;
|
|
3162
3162
|
|
|
3163
3163
|
// Get varMapped from editData or empty object
|
|
3164
|
-
const varMappedValue =
|
|
3164
|
+
const varMappedValue = varMap || {};
|
|
3165
3165
|
|
|
3166
3166
|
return {
|
|
3167
3167
|
// Preview structure (for WhatsAppPreviewContent component)
|