@capillarytech/creatives-library 9.0.13 → 9.0.14

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.
Files changed (38) hide show
  1. package/package.json +1 -1
  2. package/services/api.js +10 -0
  3. package/services/tests/api.test.js +83 -0
  4. package/v2Components/CommonTestAndPreview/UnifiedPreview/WhatsAppPreviewContent.js +5 -3
  5. package/v2Components/CommonTestAndPreview/index.js +7 -0
  6. package/v2Components/NavigationBar/index.js +27 -0
  7. package/v2Components/NavigationBar/messages.js +4 -0
  8. package/v2Components/NavigationBar/tests/index.test.js +19 -0
  9. package/v2Components/NewCallTask/index.js +6 -1
  10. package/v2Components/TemplatePreview/index.js +4 -2
  11. package/v2Containers/Cap/index.js +3 -1
  12. package/v2Containers/CommunicationFlow/CommunicationFlow.js +130 -20
  13. package/v2Containers/CommunicationFlow/CommunicationFlow.scss +154 -0
  14. package/v2Containers/CommunicationFlow/CommunicationFlowCard.js +240 -0
  15. package/v2Containers/CommunicationFlow/DemoPage.js +47 -0
  16. package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +369 -2
  17. package/v2Containers/CommunicationFlow/Tests/CommunicationFlowCard.test.js +619 -0
  18. package/v2Containers/CommunicationFlow/Tests/DemoPage.test.js +77 -0
  19. package/v2Containers/CommunicationFlow/Tests/getContentBody.test.js +933 -0
  20. package/v2Containers/CommunicationFlow/constants.js +45 -10
  21. package/v2Containers/CommunicationFlow/index.js +5 -2
  22. package/v2Containers/CommunicationFlow/messages.js +20 -0
  23. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.js +94 -31
  24. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.scss +14 -11
  25. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +1144 -32
  26. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/extractContentForPreview.js +183 -0
  27. package/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/CommunicationStrategyStep.js +3 -0
  28. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.js +39 -0
  29. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +6 -2
  30. package/v2Containers/CommunicationFlow/utils/getContentBody.js +369 -0
  31. package/v2Containers/CommunicationFlow/utils/getContentBody.scss +19 -0
  32. package/v2Containers/CommunicationFlow/utils/getEnabledSteps.js +1 -1
  33. package/v2Containers/CreativesContainer/constants.js +6 -0
  34. package/v2Containers/CreativesContainer/index.js +68 -1
  35. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +2 -2
  36. package/v2Containers/Templates/index.js +2 -2
  37. package/v2Containers/TemplatesV2/index.js +9 -1
  38. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +41 -34
@@ -5,6 +5,8 @@ import React from 'react';
5
5
  import { FormattedMessage } from 'react-intl';
6
6
  import messages from './messages';
7
7
 
8
+ export const CAMPAIGNS = 'CAMPAIGNS';
9
+
8
10
  // Step identifiers
9
11
  export const STEPS = {
10
12
  MESSAGE_TYPE: 'messageType',
@@ -25,13 +27,11 @@ export const MESSAGE_TYPES_OPTIONS = [
25
27
  export const COMMUNICATION_STRATEGIES_OPTIONS = [
26
28
  {
27
29
  value: 'SINGLE_TEMPLATE', label: <FormattedMessage {...messages.singleTemplate} />, disabled: false, isMultiChannel: false,
28
- },
29
- {
30
- value: 'CHANNEL_PRIORITY', label: <FormattedMessage {...messages.channelPriority} />, disabled: false, isMultiChannel: true,
31
- },
32
- {
33
- value: 'AB_TEST', label: <FormattedMessage {...messages.abTest} />, disabled: false, isMultiChannel: true,
34
- },
30
+ }
31
+ ];
32
+
33
+ export const DEFAULT_COMMUNICATION_STRATEGY_OPTIONS = [
34
+ COMMUNICATION_STRATEGIES_OPTIONS.find((o) => o.value === 'SINGLE_TEMPLATE'),
35
35
  ];
36
36
 
37
37
  export const CHANNEL_PRIORITY = 'CHANNEL_PRIORITY';
@@ -153,8 +153,8 @@ export const CHANNELS = [
153
153
  ];
154
154
 
155
155
  // Delivery settings - channels that show/hide sender details
156
- export const CHANNELS_WITHOUT_DELIVERY = ['MPUSH', 'MOBILEPUSH', 'INAPP', 'WEBPUSH'];
157
- export const SENDER_ID_CHANNELS = ['SMS', 'EMAIL', 'VIBER', 'ZALO', 'LINE'];
156
+ export const CHANNELS_WITHOUT_DELIVERY = ['MPUSH', 'MOBILEPUSH', 'INAPP', 'WEBPUSH', 'LINE'];
157
+ export const SENDER_ID_CHANNELS = ['SMS', 'EMAIL', 'VIBER', 'ZALO'];
158
158
  export const SENDER_NUMBER_CHANNELS = ['WHATSAPP', 'RCS'];
159
159
 
160
160
  // Dynamic Controls — default config for the Other Controls section.
@@ -180,6 +180,41 @@ export const DYNAMIC_CONTROLS_CONFIG = [
180
180
  },
181
181
  ];
182
182
 
183
+ // Channels where CommonTestAndPreview has no implementation — hide "Preview and Test" menu item
184
+ export const PREVIEW_TEST_UNSUPPORTED_CHANNELS = ['LINE', 'FACEBOOK', 'CALLTASK', 'WECHAT'];
185
+
186
+ export const CHANNEL_CONTENT_KEY_MAP = {
187
+ SMS: 'smsMessageContent',
188
+ EMAIL: 'emailMessageContent',
189
+ WHATSAPP: 'whatsappMessageContent',
190
+ MPUSH: 'mpushMessageContent',
191
+ MOBILEPUSH: 'mpushMessageContent',
192
+ INAPP: 'inAppMessageContent',
193
+ ZALO: 'zaloMessageContent',
194
+ VIBER: 'viberMessageContent',
195
+ LINE: 'lineMessageContent',
196
+ WEBPUSH: 'webPushMessageContent',
197
+ RCS: 'rcsMessageContent',
198
+ WECHAT: 'wechatMessageContent',
199
+ CALLTASK: 'callTaskMessageContent',
200
+ };
201
+
202
+ export const CHANNEL_DELIVERY_KEY_MAP = {
203
+ SMS: 'smsDeliverySettings',
204
+ EMAIL: 'emailDeliverySettings',
205
+ WHATSAPP: 'whatsappDeliverySettings',
206
+ MPUSH: 'mpushDeliverySettings',
207
+ MOBILEPUSH: 'mpushDeliverySettings',
208
+ INAPP: 'inAppDeliverySettings',
209
+ ZALO: 'zaloDeliverySettings',
210
+ VIBER: 'viberDeliverySettings',
211
+ LINE: 'lineDeliverySettings',
212
+ WEBPUSH: 'webPushDeliverySettings',
213
+ RCS: 'rcsDeliverySettings',
214
+ WECHAT: 'wechatDeliverySettings',
215
+ CALLTASK: 'callTaskDeliverySettings',
216
+ };
217
+
183
218
  // Incentive types
184
219
  export const INCENTIVE_TYPES = [
185
220
  {
@@ -197,4 +232,4 @@ export const INCENTIVE_TYPES = [
197
232
  {
198
233
  value: 'badges', label: <FormattedMessage {...messages.badges} />, isActive: false, disabled: false,
199
234
  },
200
- ];
235
+ ];
@@ -74,19 +74,21 @@ CommunicationFlowContainer.propTypes = {
74
74
  }),
75
75
  incentivesData: PropTypes.shape({
76
76
  required: PropTypes.bool,
77
+ enabled: PropTypes.bool,
77
78
  types: PropTypes.arrayOf(PropTypes.shape({
78
79
  value: PropTypes.string.isRequired,
79
- label: PropTypes.string.isRequired,
80
+ label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired,
80
81
  isActive: PropTypes.bool,
81
82
  })),
82
83
  }),
83
84
  deliverySettingsData: PropTypes.object,
85
+ showDynamicControls: PropTypes.bool,
84
86
  dynamicControlsData: PropTypes.shape({
85
- required: PropTypes.bool,
86
87
  controls: PropTypes.array,
87
88
  }),
88
89
  }),
89
90
  context: PropTypes.object, // ouId, campaignId, programId, etc.
91
+ useCCS: PropTypes.bool, // If false, skips CCS bulk-claim-approve on save. Defaults to true.
90
92
  }).isRequired,
91
93
  initialData: PropTypes.object, // for edit/preview mode
92
94
  onSave: PropTypes.func.isRequired, // (data) => void - called when user saves
@@ -100,3 +102,4 @@ CommunicationFlowContainer.defaultProps = {
100
102
  };
101
103
 
102
104
  export default CommunicationFlowContainer;
105
+ export { default as CommunicationFlowCard } from './CommunicationFlowCard';
@@ -150,6 +150,18 @@ export default {
150
150
  id: `${prefix}.addContentTemplate`,
151
151
  defaultMessage: 'Content template',
152
152
  },
153
+ addCreatives: {
154
+ id: `${prefix}.addCreatives`,
155
+ defaultMessage: 'Add creatives',
156
+ },
157
+ addCreative: {
158
+ id: `${prefix}.addCreative`,
159
+ defaultMessage: 'Add creative',
160
+ },
161
+ channel: {
162
+ id: `${prefix}.channel`,
163
+ defaultMessage: 'Channel',
164
+ },
153
165
  edit: {
154
166
  id: `${prefix}.edit`,
155
167
  defaultMessage: 'Edit',
@@ -302,6 +314,14 @@ export default {
302
314
  id: `${prefix}.dynamicControlsTitle`,
303
315
  defaultMessage: 'Other controls',
304
316
  },
317
+ yes: {
318
+ id: `${prefix}.yes`,
319
+ defaultMessage: 'Yes',
320
+ },
321
+ no: {
322
+ id: `${prefix}.no`,
323
+ defaultMessage: 'No',
324
+ },
305
325
  whatsappBusinessAccount: {
306
326
  id: `${prefix}.whatsappBusinessAccount`,
307
327
  defaultMessage: 'WhatsApp Business account',
@@ -17,11 +17,16 @@ import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
17
17
  import CapHeader from '@capillarytech/cap-ui-library/CapHeader';
18
18
  import CapCustomCard from '@capillarytech/cap-ui-library/CapCustomCard';
19
19
  import CreativesContainer from '../../../CreativesContainer';
20
+ import TestAndPreviewSlidebox from '../../../../v2Components/TestAndPreviewSlidebox';
20
21
  import { DeliverySettingsSection } from '../DeliverySettingsStep';
21
- import { CHANNELS } from '../../constants';
22
- import { VIBER, MOBILE_PUSH, MPUSH, SMS, EMAIL } from '../../../CreativesContainer/constants';
22
+ import { CHANNELS, PREVIEW_TEST_UNSUPPORTED_CHANNELS } from '../../constants';
23
+ import {
24
+ VIBER, MOBILE_PUSH, MPUSH, SMS, EMAIL, WEBPUSH, ZALO, INAPP, WHATSAPP, WECHAT, RCS, FTP, BEE,
25
+ } from '../../../CreativesContainer/constants';
26
+ import getContentBody from '../../utils/getContentBody';
27
+ import extractContentForPreview from './extractContentForPreview';
23
28
  // TemplatesV2 pane keys (for channelsToHide) - derived from CHANNELS + FTP
24
- const CREATIVES_PANE_KEYS = [...CHANNELS?.map((channel) => channel?.paneKey), 'FTP'];
29
+ const CREATIVES_PANE_KEYS = [...CHANNELS?.map((channel) => channel?.paneKey), FTP];
25
30
  // Channels that TemplatesV2 shows only when enableNewChannels includes them (not in commonChannels)
26
31
  const ENABLE_NEW_CHANNELS = CHANNELS.filter((channel) => channel.requiresEnableNewChannels).map((channel) => channel?.value);
27
32
  import messages from '../../messages';
@@ -32,6 +37,7 @@ import './ChannelSelectionStep.scss';
32
37
 
33
38
  const generateContentId = () => `content_${Date.now()}`;
34
39
 
40
+
35
41
  const ChannelSelectionStep = ({
36
42
  value,
37
43
  channels, // eslint-disable-line
@@ -46,6 +52,7 @@ const ChannelSelectionStep = ({
46
52
  error,
47
53
  intl,
48
54
  capData, // From Redux - contains user/org info needed by CouponsWrapper
55
+ config,
49
56
  }) => {
50
57
  const contentItems = value?.contentItems || [];
51
58
  const [showCreativesContainer, setShowCreativesContainer] = useState(false);
@@ -54,6 +61,8 @@ const ChannelSelectionStep = ({
54
61
  const [showChannelsMenu, setShowChannelsMenu] = useState(false);
55
62
  // Keyed by contentId (or 'standalone' for the empty-state button) so each dropdown controls its own open state
56
63
  const [showIncentivesMenuMap, setShowIncentivesMenuMap] = useState({});
64
+ const [showTestAndPreview, setShowTestAndPreview] = useState(false);
65
+ const [testAndPreviewItem, setTestAndPreviewItem] = useState(null);
57
66
  const { formatMessage } = intl || {};
58
67
 
59
68
  // Available channels (filter out hidden ones)
@@ -160,14 +169,25 @@ const ChannelSelectionStep = ({
160
169
  /** Map channel to CapCustomCard type for styling (like adiona getEngagementCardType) */
161
170
  const getCardType = (channel) => {
162
171
  const upperCaseChannel = channel?.toUpperCase();
163
- const mapping = { MOBILEPUSH: MPUSH, MPUSH: MPUSH };
164
- return mapping[upperCaseChannel] || channel;
172
+ // Return "MOBILEPUSH" so the CapCustomCard wrapper gets that CSS class (scoped mobilepush styles).
173
+ // CapCustomCard renders JSX content directly when isNewMobilePush=true, bypassing getMpushContent.
174
+ if ([MOBILE_PUSH, MPUSH].includes(upperCaseChannel)) return MOBILE_PUSH;
175
+ // INAPP and WECHAT: CapCustomCard has special-case content handlers that expect a raw template
176
+ // object (versions.base), not JSX. Returning undefined causes it to fall through to the
177
+ // default branch which renders content directly.
178
+ if ([INAPP, WECHAT].includes(upperCaseChannel)) return undefined;
179
+ return channel;
165
180
  };
166
181
 
182
+
167
183
  const handlePreviewContent = useCallback((item) => {
168
- // Placeholder for preview - can be wired to Preview slidebox when available
169
- // eslint-disable-next-line no-console
170
- console.log('Preview content', item?.contentId);
184
+ setTestAndPreviewItem(item);
185
+ setShowTestAndPreview(true);
186
+ }, []);
187
+
188
+ const handleCloseTestAndPreview = useCallback(() => {
189
+ setShowTestAndPreview(false);
190
+ setTestAndPreviewItem(null);
171
191
  }, []);
172
192
 
173
193
  const handleChannelSelect = (channelValue) => {
@@ -197,7 +217,7 @@ const ChannelSelectionStep = ({
197
217
  };
198
218
 
199
219
  const handleChannelMenuClick = ({ key }) => {
200
- // key should be the channel value (e.g., 'SMS', 'EMAIL', etc.)
220
+ // key should be the channel value (e.g., SMS, EMAIL, etc.)
201
221
  handleChannelSelect(key);
202
222
  setShowChannelsMenu(false);
203
223
  };
@@ -244,7 +264,7 @@ const ChannelSelectionStep = ({
244
264
 
245
265
  // Filter out FTP and ensure only active channels are shown
246
266
  const filteredChannels = availableChannels.filter((channel) => {
247
- const isFTP = channel.value === 'FTP' || channel.value === 'ftp';
267
+ const isFTP = [FTP, FTP.toLowerCase()].includes(channel.value);
248
268
  const isHidden = channelsToHide.includes(channel.value);
249
269
  const isDisabled = channelsToDisable.includes(channel.value);
250
270
  return !isFTP && !isHidden && channel.isActive;
@@ -303,7 +323,7 @@ const ChannelSelectionStep = ({
303
323
  const iconType = channelConfig?.iconType || 'sms';
304
324
  const channelLabel = channelConfig?.label || item.channel;
305
325
  const senderDetailsText = getSenderDetailsDisplay(item);
306
- const hasIncentiveOptions = incentivesData && availableIncentiveTypes?.some((i) => i.value !== 'coupons' && i.value !== 'points');
326
+ const hasIncentiveOptions = incentivesData?.enabled && availableIncentiveTypes?.some((incentiveType) => incentiveType.value !== 'coupons' && incentiveType.value !== 'points');
307
327
 
308
328
  const card = {
309
329
  title: (
@@ -314,9 +334,9 @@ const ChannelSelectionStep = ({
314
334
  ),
315
335
  content: (
316
336
  <CapRow className="content-card-body">
317
- <CapLabel type="label2" className="content-preview-text">
318
- {getContentPreview(item)}
319
- </CapLabel>
337
+ <CapRow className="content-preview-text">
338
+ {getContentBody(item)}
339
+ </CapRow>
320
340
  {senderDetailsText && (
321
341
  <CapRow type="flex" align="middle" justify="space-between" className="sender-details-row">
322
342
  <CapLabel type="label2">{senderDetailsText}</CapLabel>
@@ -326,6 +346,7 @@ const ChannelSelectionStep = ({
326
346
  </CapRow>
327
347
  ),
328
348
  cardType: getCardType(item.channel),
349
+ isNewMobilePush: [MOBILE_PUSH, MPUSH].includes(item.channel?.toUpperCase()),
329
350
  extra: (
330
351
  <CapDropdown
331
352
  overlay={
@@ -337,13 +358,15 @@ const ChannelSelectionStep = ({
337
358
  >
338
359
  {formatMessage(messages.edit)}
339
360
  </CapMenu.Item>
340
- <CapMenu.Item
341
- id="preview-menu-item"
342
- className="ant-dropdown-menu-item"
343
- onClick={() => handlePreviewContent(item)}
344
- >
345
- {formatMessage(messages.previewAndTest)}
346
- </CapMenu.Item>
361
+ {!PREVIEW_TEST_UNSUPPORTED_CHANNELS.includes(item?.channel?.toUpperCase()) && (
362
+ <CapMenu.Item
363
+ id="preview-menu-item"
364
+ className="ant-dropdown-menu-item"
365
+ onClick={() => handlePreviewContent(item)}
366
+ >
367
+ {formatMessage(messages.previewAndTest)}
368
+ </CapMenu.Item>
369
+ )}
347
370
  <CapMenu.Item
348
371
  id="delete-menu-item"
349
372
  onClick={() => handleDeleteContent(item.contentId)}
@@ -392,23 +415,26 @@ const ChannelSelectionStep = ({
392
415
  useLegacy
393
416
  className={`content-template-container ${contentItems?.length > 0 ? 'content-template-container--has-content' : ''}`}
394
417
  >
418
+ {contentItems?.length === 0 && (
419
+ <CapHeading type="label2" className="content-card-channel-header">
420
+ {formatMessage(messages.channel)}
421
+ </CapHeading>
422
+ )}
423
+
395
424
  {contentItems?.length === 0 ? (
396
425
  <CapRow className="content-template-section">
397
- <CapHeading type="h5">
398
- {formatMessage(messages.addMessageContentAndIncentive)}
399
- </CapHeading>
400
-
401
426
  <CapDropdown
402
- overlay={renderChannelDropdownOverlay()}
427
+ overlay={renderChannelDropdownOverlay() || <CapMenu />}
403
428
  onVisibleChange={handleChannelsVisibleChange}
404
429
  visible={showChannelsMenu}
405
430
  >
406
431
  <CapButton
407
432
  isAddBtn
408
- type="primary"
433
+ type="secondary"
409
434
  className="add-content-template-button"
435
+ onClick={() => renderChannelDropdownOverlay() !== null && setShowChannelsMenu(true)}
410
436
  >
411
- {formatMessage(messages.addContentTemplate)}
437
+ {formatMessage(messages.addCreative)}
412
438
  </CapButton>
413
439
  </CapDropdown>
414
440
  </CapRow>
@@ -420,7 +446,7 @@ const ChannelSelectionStep = ({
420
446
  )}
421
447
 
422
448
  {contentItems?.length === 0 &&
423
- incentivesData &&
449
+ incentivesData?.enabled &&
424
450
  availableIncentiveTypes?.some((i) => i.value !== 'coupons' && i.value !== 'points') && (
425
451
  <CapRow type="flex" justify="center" align="middle" className="incentive-section">
426
452
  <CapDropdown
@@ -460,12 +486,25 @@ const ChannelSelectionStep = ({
460
486
  <CreativesContainer
461
487
  key={`creatives-${selectedChannel}-${editingContentId || 'create'}`}
462
488
  channel={selectedChannelConfig.channelProp}
463
- creativesMode={editingContentId ? 'edit' : creativesMode}
489
+ creativesMode={editingContentId ? 'edit' : 'create'}
464
490
  getCreativesData={handleCreativesData}
465
491
  handleCloseCreatives={handleCloseCreatives}
466
492
  isFullMode={false}
467
493
  messageDetails={{ type: 'default' }}
468
- templateData={editingContentId ? contentItems.find((c) => c.contentId === editingContentId)?.templateData : null}
494
+ templateData={editingContentId ? (() => {
495
+ const saved = contentItems.find((c) => c.contentId === editingContentId)?.templateData;
496
+ // getTemplateData in CreativesContainer reads 'content', 'accountId', 'messageSubject' at
497
+ // top-level and needs 'type' for SlideBoxContent to set isEditWebPush. Our stored WEBPUSH
498
+ // templateData wraps those fields inside messageContent.content, so extract them here.
499
+ if (saved?.channel?.toUpperCase() === WEBPUSH && saved?.messageContent?.content) {
500
+ return { ...saved.messageContent.content, type: WEBPUSH };
501
+ }
502
+ return saved;
503
+ })() : null}
504
+ editor={(() => {
505
+ const saved = contentItems.find((c) => c.contentId === editingContentId)?.templateData;
506
+ return (saved?.is_drag_drop === true || saved?.is_drag_drop === 1) ? BEE : undefined;
507
+ })()}
469
508
  selectedOfferDetails={selectedOfferDetails}
470
509
  channelsToHide={[
471
510
  ...(channelsToHide || []),
@@ -473,10 +512,31 @@ const ChannelSelectionStep = ({
473
512
  ]}
474
513
  channelsToDisable={channelsToDisable}
475
514
  enableNewChannels={ENABLE_NEW_CHANNELS}
515
+ cap={capData}
476
516
  />
477
517
  )}
478
518
 
479
519
  {/* cap-coupons flows disabled - Coupons and Points slideboxes commented out */}
520
+
521
+ {showTestAndPreview && testAndPreviewItem && (() => {
522
+ const { content, formData, accountId, sourceAccountIdentifier, accountName } = extractContentForPreview(testAndPreviewItem);
523
+ const upperChannel = testAndPreviewItem?.channel?.toUpperCase();
524
+ const messageMetaConfigId = testAndPreviewItem?.templateData?.templateConfigs?.templateId || null;
525
+ return (
526
+ <TestAndPreviewSlidebox
527
+ show={showTestAndPreview}
528
+ onClose={handleCloseTestAndPreview}
529
+ channel={upperChannel}
530
+ content={content}
531
+ formData={formData}
532
+ messageMetaConfigId={messageMetaConfigId}
533
+ orgUnitId={config?.context?.ouId}
534
+ accountId={accountId}
535
+ sourceAccountIdentifier={sourceAccountIdentifier}
536
+ accountName={accountName}
537
+ />
538
+ );
539
+ })()}
480
540
  </>
481
541
  );
482
542
  };
@@ -500,11 +560,13 @@ ChannelSelectionStep.propTypes = {
500
560
  incentivesData: PropTypes.shape({
501
561
  types: PropTypes.array,
502
562
  required: PropTypes.bool,
563
+ enabled: PropTypes.bool,
503
564
  }),
504
565
  deliverySettingsData: PropTypes.object,
505
566
  error: PropTypes.string,
506
567
  intl: PropTypes.object.isRequired,
507
568
  capData: PropTypes.object, // Cap data from Redux (user/org info)
569
+ config: PropTypes.object,
508
570
  };
509
571
 
510
572
  ChannelSelectionStep.defaultProps = {
@@ -518,6 +580,7 @@ ChannelSelectionStep.defaultProps = {
518
580
  selectedOfferDetails: [],
519
581
  incentivesData: null,
520
582
  capData: {},
583
+ config: {},
521
584
  };
522
585
 
523
586
  export default injectIntl(ChannelSelectionStep);
@@ -111,12 +111,19 @@
111
111
  }
112
112
  }
113
113
 
114
- // Empty state styles - unchanged from original
114
+ .content-card-channel-header {
115
+ width: 100%;
116
+ padding: 1rem 0 0 1rem;
117
+ align-self: flex-start;
118
+ font-weight: bold;
119
+ }
120
+
121
+ // Empty state styles
115
122
  .content-template-section {
116
- display: flex;
117
- flex-direction: column;
118
- align-items: center;
119
- justify-content: center;
123
+ display: flex !important;
124
+ flex-direction: column !important;
125
+ align-items: center !important;
126
+ justify-content: center !important;
120
127
  height: 15.75rem;
121
128
  width: 100%;
122
129
  padding: 0 1rem;
@@ -124,12 +131,8 @@
124
131
  }
125
132
 
126
133
  .add-content-template-button {
127
- margin-top: $CAP_SPACE_16;
128
- padding: $CAP_SPACE_12;
129
- padding-right: $CAP_SPACE_16;
130
- display: flex;
131
- align-items: center;
132
- justify-content: center;
134
+ padding: $CAP_SPACE_12 $CAP_SPACE_16;
135
+ align-self: center !important;
133
136
  }
134
137
 
135
138
  .incentive-section {