@capillarytech/creatives-library 9.0.54-0 → 9.0.54-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/constants/unified.js +0 -1
- package/package.json +1 -1
- package/services/api.js +9 -0
- package/utils/common.js +0 -4
- package/v2Components/CommonTestAndPreview/index.js +0 -3
- package/v2Containers/CommunicationFlow/CommunicationFlow.js +93 -58
- package/v2Containers/CommunicationFlow/CommunicationFlowCard.js +20 -4
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +194 -49
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlowCard.test.js +16 -0
- package/v2Containers/CommunicationFlow/constants.js +47 -0
- package/v2Containers/CommunicationFlow/index.js +15 -20
- package/v2Containers/CommunicationFlow/messages.js +4 -0
- package/v2Containers/CreativesContainer/index.js +0 -2
- package/v2Containers/Rcs/constants.js +0 -1
- package/v2Containers/Rcs/index.js +1 -9
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +0 -108
- package/v2Containers/Rcs/tests/index.test.js +0 -121
- package/v2Containers/Templates/index.js +0 -20
- package/.vscode/settings.json +0 -3
package/constants/unified.js
CHANGED
|
@@ -63,7 +63,6 @@ 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';
|
|
67
66
|
export const EMBEDDED = 'embedded';
|
|
68
67
|
// --- Tag/Validation Constants ---
|
|
69
68
|
export const CARD_RELATED_TAGS = [
|
package/package.json
CHANGED
package/services/api.js
CHANGED
|
@@ -701,6 +701,15 @@ 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
|
+
|
|
704
713
|
export const getCentralCommsMetaIds = (metaIds, metaType = TRANSACTION) => {
|
|
705
714
|
const url = `${API_ENDPOINT}/common/central-comms/meta-id/${metaType}?metaIds=${metaIds}`;
|
|
706
715
|
return request(url, getAPICallObject('GET', null, false, false, false, true));
|
package/utils/common.js
CHANGED
|
@@ -99,10 +99,6 @@ 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
|
-
);
|
|
106
102
|
|
|
107
103
|
export const hasGiftVoucherFeature = Auth.hasFeatureAccess.bind(
|
|
108
104
|
null,
|
|
@@ -1275,7 +1275,6 @@ const CommonTestAndPreview = (props) => {
|
|
|
1275
1275
|
...(suggestionsFormattedForTestMeta.length > 0 && {
|
|
1276
1276
|
suggestions: suggestionsFormattedForTestMeta,
|
|
1277
1277
|
}),
|
|
1278
|
-
...(singleRcsCardPayload?.cardVarMapped && { cardVarMapped: singleRcsCardPayload.cardVarMapped }),
|
|
1279
1278
|
};
|
|
1280
1279
|
});
|
|
1281
1280
|
// Use the component-level smsFallbackContent prop (has rcsSmsFallbackVarMapped) so DLT
|
|
@@ -1342,11 +1341,9 @@ const CommonTestAndPreview = (props) => {
|
|
|
1342
1341
|
...(cardContentForTestMetaApi.length > 0 && { cardContent: cardContentForTestMetaApi }),
|
|
1343
1342
|
};
|
|
1344
1343
|
|
|
1345
|
-
const vfTemplateId = rcsContentFromForm?.vfTemplateId;
|
|
1346
1344
|
const rcsMessageContent = {
|
|
1347
1345
|
channel: CHANNELS.RCS,
|
|
1348
1346
|
...(accountIdForMeta && { accountId: accountIdForMeta }),
|
|
1349
|
-
...(vfTemplateId && { templateId: vfTemplateId }),
|
|
1350
1347
|
rcsRichCardContent,
|
|
1351
1348
|
...(smsFallBackContent && { smsFallBackContent }),
|
|
1352
1349
|
};
|
|
@@ -17,10 +17,11 @@ 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';
|
|
20
21
|
// import injectSaga from '../../utils/injectSaga'; // cap-coupons flows disabled
|
|
21
22
|
// import injectReducer from '../../utils/injectReducer';
|
|
22
23
|
import { makeSelectAuthenticated } from '../Cap/selectors';
|
|
23
|
-
import {
|
|
24
|
+
import { createCommDefinition } from '../../services/api';
|
|
24
25
|
import DynamicControlsStep from './steps/DynamicControlsStep';
|
|
25
26
|
import MessageTypeStep from './steps/MessageTypeStep';
|
|
26
27
|
import CommunicationStrategyStep from './steps/CommunicationStrategyStep';
|
|
@@ -35,9 +36,10 @@ import {
|
|
|
35
36
|
CHANNELS,
|
|
36
37
|
INCENTIVE_TYPES,
|
|
37
38
|
DYNAMIC_CONTROLS_CONFIG,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
CCS_STRATEGY_TYPE_SINGLE,
|
|
40
|
+
CCS_CHANNEL_CONTENT_KEY_MAP,
|
|
41
|
+
CCS_CHANNEL_DELIVERY_KEY_MAP,
|
|
42
|
+
CCS_CHANNEL_NAME_MAP,
|
|
41
43
|
} from './constants';
|
|
42
44
|
import { getEnabledSteps } from './utils/getEnabledSteps';
|
|
43
45
|
import messages from './messages';
|
|
@@ -64,6 +66,25 @@ const hasDeliverySettingForChannel = (channelSetting, channel) => {
|
|
|
64
66
|
return !!settings && Object.values(settings).some((v) => v !== null && v !== '' && v !== undefined);
|
|
65
67
|
};
|
|
66
68
|
|
|
69
|
+
// createCommDefinition resolves (rather than rejects) with the CCS error envelope
|
|
70
|
+
// for 4xx/5xx responses — see api.js's request()/checkStatus. A duplicate
|
|
71
|
+
// referenceId within the org comes back as this specific 409.
|
|
72
|
+
const isDuplicateReferenceIdError = (res) => res?.success === false && res?.status?.message === 'REFERENCE_ID_EXISTS';
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* CCS requires referenceId on create; the consumer-supplied Alert/comm name is
|
|
76
|
+
* mandatory but referenceId is optional in every consumer's own form. Generate
|
|
77
|
+
* a stable fallback from the name rather than failing the save.
|
|
78
|
+
*/
|
|
79
|
+
const buildCcsReferenceId = (name) => {
|
|
80
|
+
const slug = (name || 'COMM')
|
|
81
|
+
.trim()
|
|
82
|
+
.toUpperCase()
|
|
83
|
+
.replace(/[^A-Z0-9]+/g, '_')
|
|
84
|
+
.replace(/^_+|_+$/g, '') || 'COMM';
|
|
85
|
+
return `${slug}_${Date.now()}`;
|
|
86
|
+
};
|
|
87
|
+
|
|
67
88
|
const CommunicationFlow = ({
|
|
68
89
|
config,
|
|
69
90
|
initialData,
|
|
@@ -91,6 +112,7 @@ const CommunicationFlow = ({
|
|
|
91
112
|
};
|
|
92
113
|
});
|
|
93
114
|
const [validationErrors, setValidationErrors] = useState({});
|
|
115
|
+
const [saveError, setSaveError] = useState(null);
|
|
94
116
|
|
|
95
117
|
// Memoize enabled steps
|
|
96
118
|
const enabledSteps = useMemo(() => getEnabledSteps(config), [config]);
|
|
@@ -150,74 +172,82 @@ const CommunicationFlow = ({
|
|
|
150
172
|
}, []);
|
|
151
173
|
|
|
152
174
|
const handleSave = useCallback(async () => {
|
|
175
|
+
setSaveError(null);
|
|
153
176
|
const aggregatedData = getAggregatedData();
|
|
154
177
|
const shouldUseCCS = config?.useCCS !== false;
|
|
178
|
+
let ccsCommDefinition = null;
|
|
155
179
|
|
|
156
180
|
if (shouldUseCCS) {
|
|
157
|
-
const
|
|
158
|
-
const
|
|
159
|
-
|
|
181
|
+
const isMultiChannel = [CHANNEL_PRIORITY, AB_TEST].includes(aggregatedData.communicationStrategy);
|
|
182
|
+
const contentItem = (aggregatedData.contentItems || [])[0];
|
|
183
|
+
// Consumer-supplied name (e.g. CapNotify's Alert Name field) — CommunicationFlow
|
|
184
|
+
// has no name input of its own, so this is mandatory input from config.context.
|
|
185
|
+
const name = config?.context?.name;
|
|
160
186
|
|
|
161
|
-
|
|
162
|
-
|
|
187
|
+
// CCS create is SINGLE-strategy only this phase (D1); CHANNEL_PRIORITY/AB_TEST
|
|
188
|
+
// carry multiple content items with no CCS equivalent yet.
|
|
189
|
+
if (!isMultiChannel && contentItem && name) {
|
|
190
|
+
const rawChannel = (contentItem.channel || '').toUpperCase();
|
|
191
|
+
const channel = CCS_CHANNEL_NAME_MAP[rawChannel] || rawChannel;
|
|
192
|
+
const contentKey = CCS_CHANNEL_CONTENT_KEY_MAP[channel];
|
|
193
|
+
const deliveryKey = CCS_CHANNEL_DELIVERY_KEY_MAP[channel];
|
|
194
|
+
const { dynamicControls = {} } = aggregatedData;
|
|
195
|
+
const channelSettings = aggregatedData.deliverySetting?.channelSetting?.[channel] || {};
|
|
196
|
+
const referenceId = config?.context?.referenceId || buildCcsReferenceId(name);
|
|
163
197
|
|
|
164
|
-
|
|
165
|
-
|
|
198
|
+
const payload = {
|
|
199
|
+
referenceId,
|
|
200
|
+
name,
|
|
201
|
+
description: config?.context?.description || undefined,
|
|
202
|
+
strategyType: CCS_STRATEGY_TYPE_SINGLE,
|
|
203
|
+
settings: {
|
|
204
|
+
additionalSettings: {
|
|
205
|
+
useTinyUrl: dynamicControls.useTinyUrl ?? false,
|
|
206
|
+
encryptUrl: dynamicControls.sendToControlCustomers ?? false,
|
|
207
|
+
linkTrackingEnabled: dynamicControls.overrideDailyLimit ?? false,
|
|
208
|
+
userSubscriptionDisabled: dynamicControls.sendToBrandPocs ?? false,
|
|
209
|
+
},
|
|
210
|
+
executionParams: {},
|
|
211
|
+
},
|
|
212
|
+
singleChannelStrategy: {
|
|
213
|
+
variant: {
|
|
214
|
+
channel,
|
|
215
|
+
...(contentKey && { [contentKey]: contentItem.templateData }),
|
|
216
|
+
...(deliveryKey && { [deliveryKey]: { channelSettings: { channel, ...channelSettings } } }),
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
};
|
|
166
220
|
|
|
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) {
|
|
175
221
|
try {
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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);
|
|
222
|
+
const res = await createCommDefinition(payload);
|
|
223
|
+
if (isDuplicateReferenceIdError(res)) {
|
|
224
|
+
// Duplicate referenceId in this org — block the save so the user can
|
|
225
|
+
// change it, rather than silently proceeding without a CCS comm.
|
|
226
|
+
setSaveError(formatMessage(messages.duplicateReferenceIdError));
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const data = res?.response?.data;
|
|
230
|
+
if (data?.id) {
|
|
231
|
+
ccsCommDefinition = {
|
|
232
|
+
id: data.id,
|
|
233
|
+
referenceId: data.referenceId,
|
|
234
|
+
version: data.version?.version ?? 1,
|
|
235
|
+
status: data.status,
|
|
236
|
+
};
|
|
213
237
|
}
|
|
214
238
|
} catch (error) {
|
|
215
|
-
console.error('[CommunicationFlow] CCS
|
|
239
|
+
console.error('[CommunicationFlow] CCS createCommDefinition error:', error);
|
|
216
240
|
}
|
|
241
|
+
} else if (!name) {
|
|
242
|
+
console.warn('[CommunicationFlow] Skipping CCS createCommDefinition — config.context.name is required');
|
|
243
|
+
} else if (isMultiChannel) {
|
|
244
|
+
console.warn('[CommunicationFlow] Skipping CCS createCommDefinition — CHANNEL_PRIORITY/AB_TEST strategies are not supported by CCS create yet (SINGLE only)');
|
|
245
|
+
} else if (!contentItem) {
|
|
246
|
+
console.warn('[CommunicationFlow] Skipping CCS createCommDefinition — no content item found (contentItems is empty)');
|
|
217
247
|
}
|
|
218
248
|
}
|
|
219
249
|
|
|
220
|
-
onSave(aggregatedData);
|
|
250
|
+
onSave(ccsCommDefinition ? { ...aggregatedData, ccsCommDefinition } : aggregatedData);
|
|
221
251
|
}, [getAggregatedData, config, onSave]);
|
|
222
252
|
|
|
223
253
|
// Call onChange callback when stepData changes
|
|
@@ -317,6 +347,11 @@ const CommunicationFlow = ({
|
|
|
317
347
|
{renderSteps()}
|
|
318
348
|
{onSave && (
|
|
319
349
|
<CapRow useLegacy className="communication-flow-container__footer">
|
|
350
|
+
{saveError && (
|
|
351
|
+
<CapLabel type="label2" className="communication-flow-container__save-error">
|
|
352
|
+
{saveError}
|
|
353
|
+
</CapLabel>
|
|
354
|
+
)}
|
|
320
355
|
<CapButton type="primary" onClick={handleSave} disabled={isSaveDisabled}>
|
|
321
356
|
{formatMessage(messages.save)}
|
|
322
357
|
</CapButton>
|
|
@@ -19,6 +19,7 @@ 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';
|
|
22
23
|
import CommunicationFlow from './index';
|
|
23
24
|
import { CHANNELS, DEFAULT_COMMUNICATION_STRATEGY_OPTIONS, DYNAMIC_CONTROLS_CONFIG } from './constants';
|
|
24
25
|
import {
|
|
@@ -74,6 +75,8 @@ const CommunicationFlowCard = ({
|
|
|
74
75
|
onChange,
|
|
75
76
|
cap,
|
|
76
77
|
intl,
|
|
78
|
+
disabled,
|
|
79
|
+
disabledTooltip,
|
|
77
80
|
}) => {
|
|
78
81
|
const { formatMessage } = intl || {};
|
|
79
82
|
const [showSlideBox, setShowSlideBox] = useState(false);
|
|
@@ -91,8 +94,9 @@ const CommunicationFlowCard = ({
|
|
|
91
94
|
}, [onCancel]);
|
|
92
95
|
|
|
93
96
|
const handleOpen = useCallback(() => {
|
|
97
|
+
if (disabled) return;
|
|
94
98
|
setShowSlideBox(true);
|
|
95
|
-
}, []);
|
|
99
|
+
}, [disabled]);
|
|
96
100
|
|
|
97
101
|
const firstItem = savedData?.contentItems?.[0];
|
|
98
102
|
const channelConfig = firstItem
|
|
@@ -182,9 +186,17 @@ const CommunicationFlowCard = ({
|
|
|
182
186
|
<CapImage src={addCreativesIllustration} />
|
|
183
187
|
</CapColumn>
|
|
184
188
|
<CapColumn span={14} className="empty-card-action-col">
|
|
185
|
-
|
|
186
|
-
{
|
|
187
|
-
|
|
189
|
+
{disabled && disabledTooltip ? (
|
|
190
|
+
<CapTooltip title={disabledTooltip}>
|
|
191
|
+
<CapButton type="secondary" disabled>
|
|
192
|
+
{formatMessage(messages.addCreatives)}
|
|
193
|
+
</CapButton>
|
|
194
|
+
</CapTooltip>
|
|
195
|
+
) : (
|
|
196
|
+
<CapButton type="secondary" onClick={handleOpen} disabled={disabled}>
|
|
197
|
+
{formatMessage(messages.addCreatives)}
|
|
198
|
+
</CapButton>
|
|
199
|
+
)}
|
|
188
200
|
</CapColumn>
|
|
189
201
|
</CapRow>
|
|
190
202
|
</CapCard>
|
|
@@ -227,6 +239,8 @@ CommunicationFlowCard.propTypes = {
|
|
|
227
239
|
onChange: PropTypes.func,
|
|
228
240
|
cap: PropTypes.object,
|
|
229
241
|
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
|
|
230
244
|
};
|
|
231
245
|
|
|
232
246
|
CommunicationFlowCard.defaultProps = {
|
|
@@ -235,6 +249,8 @@ CommunicationFlowCard.defaultProps = {
|
|
|
235
249
|
onCancel: null,
|
|
236
250
|
onChange: null,
|
|
237
251
|
cap: null,
|
|
252
|
+
disabled: false,
|
|
253
|
+
disabledTooltip: null,
|
|
238
254
|
};
|
|
239
255
|
|
|
240
256
|
export default injectIntl(CommunicationFlowCard);
|