@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.
@@ -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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "9.0.54-alpha.4",
4
+ "version": "9.0.54",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
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
- { this.state.translationLang !== 'ja-JP' && (
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 { createCommDefinition, editCommDefinition } from '../../services/api';
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
- CCS_STRATEGY_TYPE_SINGLE,
40
- CCS_CHANNEL_CONTENT_KEY_MAP,
41
- CCS_CHANNEL_DELIVERY_KEY_MAP,
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 isMultiChannel = [CHANNEL_PRIORITY, AB_TEST].includes(aggregatedData.communicationStrategy);
183
- const contentItem = (aggregatedData.contentItems || [])[0];
184
- // Consumer-supplied name (e.g. CapNotify's Alert Name field) — CommunicationFlow
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
- // CCS create is SINGLE-strategy only this phase (D1); CHANNEL_PRIORITY/AB_TEST
189
- // carry multiple content items with no CCS equivalent yet.
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
- const payload = {
202
- referenceId,
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
- // Editing an existing (e.g. rejected) alert: config.context.existingCommDefinitionId
229
- // opens a new DRAFT version on that SAME CommDefinition instead of creating a
230
- // brand-new one, so the alert keeps its id/referenceId across the edit.
231
- const { existingCommDefinitionId } = config?.context || {};
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 res = existingCommDefinitionId
235
- ? await editCommDefinition(existingCommDefinitionId, {
236
- strategyType: payload.strategyType,
237
- settings: payload.settings,
238
- singleChannelStrategy: payload.singleChannelStrategy,
239
- })
240
- : await createCommDefinition(payload);
241
- if (isDuplicateReferenceIdError(res)) {
242
- // Duplicate referenceId in this org — block the save so the user can
243
- // change it, rather than silently proceeding without a CCS comm.
244
- setSaveError(formatMessage(messages.duplicateReferenceIdError));
245
- return;
246
- }
247
- const data = res?.response?.data;
248
- if (existingCommDefinitionId) {
249
- if (data) {
250
- ccsCommDefinition = {
251
- id: existingCommDefinitionId,
252
- referenceId: referenceId || data.referenceId,
253
- version: data.version?.version ?? data.version ?? 1,
254
- status: data.status,
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
- } else if (data?.id) {
258
- ccsCommDefinition = {
259
- id: data.id,
260
- referenceId: data.referenceId,
261
- version: data.version?.version ?? 1,
262
- status: data.status,
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 createCommDefinition error:', error);
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(ccsCommDefinition ? { ...aggregatedData, ccsCommDefinition } : aggregatedData);
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?.senderId),
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
- }, [disabled]);
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
- {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
- )}
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);