@capillarytech/creatives-library 9.0.54-alpha.4 → 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 -20
- package/utils/common.js +4 -0
- package/v2Components/FormBuilder/Classic.js +2 -2
- package/v2Containers/CommunicationFlow/CommunicationFlow.js +56 -118
- package/v2Containers/CommunicationFlow/CommunicationFlowCard.js +5 -21
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +49 -365
- 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,26 +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
|
-
// Opens a new DRAFT version with the given content on an EXISTING
|
|
714
|
-
// CommDefinition (same id/referenceId) — used by CommunicationFlow's edit-mode
|
|
715
|
-
// save (re-editing a rejected alert) instead of createCommDefinition, so the
|
|
716
|
-
// alert keeps its identity rather than becoming a brand-new CommDefinition.
|
|
717
|
-
// Route verified against a real Postman sample against Veyron's own
|
|
718
|
-
// /commdefinition/{id}/edit endpoint.
|
|
719
|
-
export const editCommDefinition = (commDefinitionId, payload) => {
|
|
720
|
-
const url = `${API_ENDPOINT}/comm-definitions/${commDefinitionId}/edit`;
|
|
721
|
-
return request(url, getAPICallObject('POST', payload, false, false, false, true));
|
|
722
|
-
};
|
|
723
|
-
|
|
724
704
|
export const getCentralCommsMetaIds = (metaIds, metaType = TRANSACTION) => {
|
|
725
705
|
const url = `${API_ENDPOINT}/common/central-comms/meta-id/${metaType}?metaIds=${metaIds}`;
|
|
726
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,108 +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
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
167
|
+
const additionalSettings = {
|
|
168
|
+
useTinyUrl: dynamicControls.useTinyUrl ?? false,
|
|
169
|
+
encryptUrl: dynamicControls.sendToControlCustomers ?? false,
|
|
170
|
+
linkTrackingEnabled: dynamicControls.overrideDailyLimit ?? false,
|
|
171
|
+
userSubscriptionDisabled: dynamicControls.sendToBrandPocs ?? false,
|
|
172
|
+
};
|
|
232
173
|
|
|
174
|
+
if (contentItems.length > 0) {
|
|
233
175
|
try {
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
+
},
|
|
255
201
|
};
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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);
|
|
264
213
|
}
|
|
265
214
|
} catch (error) {
|
|
266
|
-
console.error('[CommunicationFlow] CCS
|
|
215
|
+
console.error('[CommunicationFlow] CCS createCentralCommsMetaId error:', error);
|
|
267
216
|
}
|
|
268
|
-
} else if (!name) {
|
|
269
|
-
console.warn('[CommunicationFlow] Skipping CCS createCommDefinition — config.context.name is required');
|
|
270
|
-
} else if (isMultiChannel) {
|
|
271
|
-
console.warn('[CommunicationFlow] Skipping CCS createCommDefinition — CHANNEL_PRIORITY/AB_TEST strategies are not supported by CCS create yet (SINGLE only)');
|
|
272
|
-
} else if (!contentItem) {
|
|
273
|
-
console.warn('[CommunicationFlow] Skipping CCS createCommDefinition — no content item found (contentItems is empty)');
|
|
274
217
|
}
|
|
275
218
|
}
|
|
276
219
|
|
|
277
|
-
onSave(
|
|
220
|
+
onSave(aggregatedData);
|
|
278
221
|
}, [getAggregatedData, config, onSave]);
|
|
279
222
|
|
|
280
223
|
// Call onChange callback when stepData changes
|
|
@@ -374,11 +317,6 @@ const CommunicationFlow = ({
|
|
|
374
317
|
{renderSteps()}
|
|
375
318
|
{onSave && (
|
|
376
319
|
<CapRow useLegacy className="communication-flow-container__footer">
|
|
377
|
-
{saveError && (
|
|
378
|
-
<CapLabel type="label2" className="communication-flow-container__save-error">
|
|
379
|
-
{saveError}
|
|
380
|
-
</CapLabel>
|
|
381
|
-
)}
|
|
382
320
|
<CapButton type="primary" onClick={handleSave} disabled={isSaveDisabled}>
|
|
383
321
|
{formatMessage(messages.save)}
|
|
384
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);
|