@capillarytech/creatives-library 9.0.14-beta.0 → 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 (83) hide show
  1. package/constants/unified.js +0 -3
  2. package/package.json +1 -1
  3. package/services/api.js +10 -0
  4. package/services/tests/api.test.js +83 -0
  5. package/utils/common.js +0 -8
  6. package/v2Components/CommonTestAndPreview/UnifiedPreview/WhatsAppPreviewContent.js +5 -3
  7. package/v2Components/CommonTestAndPreview/index.js +7 -0
  8. package/v2Components/FormBuilder/_formBuilder.scss +0 -5
  9. package/v2Components/FormBuilder/index.js +4479 -41
  10. package/v2Components/NavigationBar/index.js +27 -0
  11. package/v2Components/NavigationBar/messages.js +4 -0
  12. package/v2Components/NavigationBar/tests/index.test.js +19 -0
  13. package/v2Components/NewCallTask/index.js +6 -1
  14. package/v2Components/TemplatePreview/index.js +4 -2
  15. package/v2Containers/Cap/index.js +3 -1
  16. package/v2Containers/CommunicationFlow/CommunicationFlow.js +130 -20
  17. package/v2Containers/CommunicationFlow/CommunicationFlow.scss +154 -0
  18. package/v2Containers/CommunicationFlow/CommunicationFlowCard.js +240 -0
  19. package/v2Containers/CommunicationFlow/DemoPage.js +47 -0
  20. package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +369 -2
  21. package/v2Containers/CommunicationFlow/Tests/CommunicationFlowCard.test.js +619 -0
  22. package/v2Containers/CommunicationFlow/Tests/DemoPage.test.js +77 -0
  23. package/v2Containers/CommunicationFlow/Tests/getContentBody.test.js +933 -0
  24. package/v2Containers/CommunicationFlow/constants.js +45 -10
  25. package/v2Containers/CommunicationFlow/index.js +5 -2
  26. package/v2Containers/CommunicationFlow/messages.js +20 -0
  27. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.js +94 -31
  28. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.scss +14 -11
  29. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +1144 -32
  30. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/extractContentForPreview.js +183 -0
  31. package/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/CommunicationStrategyStep.js +3 -0
  32. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.js +39 -0
  33. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +6 -2
  34. package/v2Containers/CommunicationFlow/utils/getContentBody.js +369 -0
  35. package/v2Containers/CommunicationFlow/utils/getContentBody.scss +19 -0
  36. package/v2Containers/CommunicationFlow/utils/getEnabledSteps.js +1 -1
  37. package/v2Containers/CreativesContainer/constants.js +6 -0
  38. package/v2Containers/CreativesContainer/index.js +68 -1
  39. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +2 -2
  40. package/v2Containers/Templates/index.js +2 -2
  41. package/v2Containers/TemplatesV2/index.js +9 -1
  42. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +41 -34
  43. package/v2Components/FormBuilder/Classic.js +0 -4487
  44. package/v2Components/FormBuilder/Functional/FormBuilderShell.js +0 -371
  45. package/v2Components/FormBuilder/Functional/channels/registry.js +0 -17
  46. package/v2Components/FormBuilder/Functional/channels/sms/buildSubmitPayload.js +0 -9
  47. package/v2Components/FormBuilder/Functional/channels/sms/config.js +0 -30
  48. package/v2Components/FormBuilder/Functional/channels/sms/getEditorErrorDescriptor.js +0 -46
  49. package/v2Components/FormBuilder/Functional/channels/sms/getLiquidContent.js +0 -13
  50. package/v2Components/FormBuilder/Functional/channels/sms/index.js +0 -22
  51. package/v2Components/FormBuilder/Functional/channels/sms/tests/getEditorErrorDescriptor.test.js +0 -52
  52. package/v2Components/FormBuilder/Functional/channels/sms/tests/getLiquidContent.test.js +0 -25
  53. package/v2Components/FormBuilder/Functional/channels/sms/tests/validate.test.js +0 -87
  54. package/v2Components/FormBuilder/Functional/channels/sms/validate.js +0 -89
  55. package/v2Components/FormBuilder/Functional/constants.js +0 -42
  56. package/v2Components/FormBuilder/Functional/core/schema/fieldRegistry.js +0 -38
  57. package/v2Components/FormBuilder/Functional/core/schema/initializeFormState.js +0 -85
  58. package/v2Components/FormBuilder/Functional/core/store/formReducer.js +0 -81
  59. package/v2Components/FormBuilder/Functional/core/store/selectors.js +0 -30
  60. package/v2Components/FormBuilder/Functional/core/store/toLegacyFormData.js +0 -91
  61. package/v2Components/FormBuilder/Functional/index.js +0 -26
  62. package/v2Components/FormBuilder/Functional/layout/FieldSlot.js +0 -59
  63. package/v2Components/FormBuilder/Functional/layout/SchemaForm.js +0 -31
  64. package/v2Components/FormBuilder/Functional/layout/Section.js +0 -116
  65. package/v2Components/FormBuilder/Functional/renderers/smsRenderers.js +0 -258
  66. package/v2Components/FormBuilder/Functional/tests/channelRegistry.test.js +0 -21
  67. package/v2Components/FormBuilder/Functional/tests/fieldRegistry.test.js +0 -65
  68. package/v2Components/FormBuilder/Functional/tests/fieldSlot.test.js +0 -97
  69. package/v2Components/FormBuilder/Functional/tests/fixtures/smsParityCases.js +0 -192
  70. package/v2Components/FormBuilder/Functional/tests/formReducer.test.js +0 -129
  71. package/v2Components/FormBuilder/Functional/tests/initializeFormState.test.js +0 -132
  72. package/v2Components/FormBuilder/Functional/tests/schemaForm.test.js +0 -40
  73. package/v2Components/FormBuilder/Functional/tests/section.test.js +0 -99
  74. package/v2Components/FormBuilder/Functional/tests/selectors.test.js +0 -67
  75. package/v2Components/FormBuilder/Functional/tests/sms.crossFlowParity.test.js +0 -155
  76. package/v2Components/FormBuilder/Functional/tests/sms.liquid.test.js +0 -172
  77. package/v2Components/FormBuilder/Functional/tests/sms.rollout.test.js +0 -122
  78. package/v2Components/FormBuilder/Functional/tests/sms.shell.parity.test.js +0 -329
  79. package/v2Components/FormBuilder/Functional/tests/smsRenderers.test.js +0 -160
  80. package/v2Components/FormBuilder/Functional/tests/toLegacyFormData.test.js +0 -95
  81. package/v2Components/FormBuilder/tests/__snapshots__/sms.characterization.test.js.snap +0 -114
  82. package/v2Components/FormBuilder/tests/entryGate.test.js +0 -106
  83. package/v2Components/FormBuilder/tests/sms.characterization.test.js +0 -336
@@ -67,11 +67,23 @@ export class NavigationBar extends React.Component {
67
67
  return {
68
68
  selectedProduct: formatMessage(messages.loyaltyProgram),
69
69
  helpUrl: LOYALTY_HELP_URL,
70
+ settingsIcon: {
71
+ iconType: 'settings',
72
+ key: 'settings',
73
+ placement: 'bottomRight',
74
+ className: 'navigation-setting-icon',
75
+ toolTip: formatMessage(messages.loyaltyCreativeSettings),
76
+ },
70
77
  };
71
78
  default:
72
79
  return {
73
80
  selectedProduct: formatMessage(messages.selectedProductDefault),
74
81
  helpUrl: HELP_URL,
82
+ settingsIcon: {
83
+ iconType: 'settings',
84
+ key: 'settings',
85
+ onClickHandler: this.onSettingsIconClick,
86
+ },
75
87
  };
76
88
  }
77
89
  }
@@ -83,6 +95,16 @@ export class NavigationBar extends React.Component {
83
95
  }
84
96
  };
85
97
 
98
+ onSettingsIconClick = () => {
99
+ const { settingsUrl } = this.props;
100
+ if (settingsUrl) {
101
+ const stateObj = {
102
+ page: "settings",
103
+ };
104
+ window.history.pushState(stateObj, 'Settings', window.location.replace(settingsUrl));
105
+ }
106
+ };
107
+
86
108
  onHelpIconClick = () => {
87
109
  const { helpUrl } = this.state;
88
110
  if (helpUrl) {
@@ -91,6 +113,7 @@ export class NavigationBar extends React.Component {
91
113
  };
92
114
 
93
115
  getTopbarIcons = (showDocumentationBot = false) => {
116
+ const { settingsIcon } = this.state;
94
117
  const ICONS = [
95
118
  {
96
119
  iconType: 'help',
@@ -98,6 +121,9 @@ export class NavigationBar extends React.Component {
98
121
  onClickHandler: this.onHelpIconClick,
99
122
  },
100
123
  ];
124
+ if (settingsIcon) {
125
+ ICONS.push(settingsIcon);
126
+ }
101
127
  return showDocumentationBot ? ICONS.slice(1) : ICONS; // If showDocumentationBot is true, help icon will be replaced by Aira icon on UI
102
128
  };
103
129
 
@@ -196,6 +222,7 @@ NavigationBar.propTypes = {
196
222
  topbarMenuData: PropTypes.array,
197
223
  logout: PropTypes.func,
198
224
  changeOrg: PropTypes.func,
225
+ settingsUrl: PropTypes.string,
199
226
  children: PropTypes.node,
200
227
  orgSettingsUrl: PropTypes.string,
201
228
  intl: intlShape.isRequired,
@@ -65,5 +65,9 @@ export default defineMessages({
65
65
  "selectOrganization": {
66
66
  id: `${scope}.selectOrganization`,
67
67
  defaultMessage: 'Select organization',
68
+ },
69
+ "loyaltyCreativeSettings": {
70
+ id: `${scope}.loyaltyCreativeSettings`,
71
+ defaultMessage: 'There are no settings for creatives within Loyalty+. Channel-related settings are available within organisation settings',
68
72
  }
69
73
  });
@@ -71,4 +71,23 @@ describe('NavigationBar', () => {
71
71
  const loyaltyProduct = screen.getByText('Loyalty+');
72
72
  expect(loyaltyProduct).toBeInTheDocument();
73
73
  });
74
+
75
+ // Regression: AntD v3 -> v6 migration (CAP-183930) dropped the settings (gear)
76
+ // icon from the top nav. These tests ensure the gear keeps rendering.
77
+ it('Should render the settings (gear) icon in the top nav bar', () => {
78
+ const updatedProps = cloneDeep(props);
79
+ updatedProps.location.pathname = '/creatives/ui/v2';
80
+ updatedProps.settingsUrl = '/campaigns/ui/creatives/settings/message';
81
+ renderComponent(updatedProps);
82
+ const settingsIcon = document.querySelector('.cap-icon-v2-settings');
83
+ expect(settingsIcon).toBeInTheDocument();
84
+ });
85
+
86
+ it('Should render the settings (gear) icon for the loyalty product', () => {
87
+ const updatedProps = cloneDeep(props);
88
+ updatedProps.location.pathname = '/creatives/ui/v2/loyalty';
89
+ renderComponent(updatedProps);
90
+ const settingsIcon = document.querySelector('.cap-icon-v2-settings.navigation-setting-icon');
91
+ expect(settingsIcon).toBeInTheDocument();
92
+ });
74
93
  });
@@ -16,6 +16,7 @@ import CallTaskPreview from '../CallTaskPreview';
16
16
  import messages from './messages';
17
17
  import './_newCallTask.scss';
18
18
  import { hasStore2DoorFeature } from '../../utils/common';
19
+ import { EDIT } from '../../constants/unified';
19
20
 
20
21
  const CALL = 'CALL';
21
22
  const STOREMAX_WHATSAPP = 'STOREMAX_WHATSAPP';
@@ -27,7 +28,11 @@ class NewCallTask extends React.Component { // eslint-disable-line react/prefer-
27
28
 
28
29
  getInitialState = () => {
29
30
  const { templateData, messageDetails, mode } = this.props;
30
- const actionType = mode === 'edit' ? messageDetails.messageContent.message_content_id_1.callTaskActionType : CALL;
31
+ const actionType = mode === EDIT
32
+ ? (messageDetails?.messageContent?.message_content_id_1?.callTaskActionType
33
+ || templateData?.callTaskActionType
34
+ || CALL)
35
+ : CALL;
31
36
  return {
32
37
  subject: templateData && templateData.subject ? templateData.subject : '',
33
38
  messageBody: templateData && templateData.messageBody ? templateData.messageBody : '',
@@ -47,7 +47,7 @@ import { handlePreviewInNewTab } from '../../utils/common';
47
47
  import { TEMPLATE, IMAGE_CAROUSEL, IMAGE, STICKER, TEXT, IMAGE_MAP, VIDEO } from '../../v2Containers/Line/Container/constants';
48
48
  import CapFacebookPreview from '../../v2Containers/CapFacebookPreview';
49
49
  import WhatsappStatusContainer from '../WhatsappStatusContainer';
50
- import { getWhatsappQuickReply, getWhatsappCarouselButtonView } from '../../v2Containers/Whatsapp/utils';
50
+ import { getWhatsappQuickReply, getWhatsappCarouselButtonView, getWhatsappDocPreview } from '../../v2Containers/Whatsapp/utils';
51
51
  import { QUICK_REPLY, WHATSAPP_CATEGORIES, PHONE_NUMBER, TEMPLATE_VARIABLE_REGEX } from '../../v2Containers/Whatsapp/constants';
52
52
  import { RCS_BUTTON_TYPES, LEFT, HORIZONTAL, VERTICAL, RIGHT} from '../../v2Containers/Rcs/constants';
53
53
  import { ANDROID, INAPP_MESSAGE_LAYOUT_TYPES } from '../../v2Containers/InApp/constants';
@@ -1234,7 +1234,9 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
1234
1234
  )}
1235
1235
  {content?.docPreview && (
1236
1236
  <div className="whatsapp-image">
1237
- {content?.docPreview}
1237
+ {React.isValidElement(content.docPreview)
1238
+ ? content.docPreview
1239
+ : getWhatsappDocPreview(content.docPreview)}
1238
1240
  </div>
1239
1241
  )}
1240
1242
  {content?.templateHeaderPreview || ""}
@@ -18,7 +18,7 @@ import * as locationActions from '../LanguageProvider/actions';
18
18
  import * as appActions from '../App/actions';
19
19
  import config from '../../config/app';
20
20
  import NavigationBar from '../../v2Components/NavigationBar';
21
- import { publicPath } from '../../config/path';
21
+ import { engagePlusPublicPath, publicPath } from '../../config/path';
22
22
  import { GTM_TRACKING_ID, CREATIVES_UI_VIEW, FAILURE } from '../App/constants';
23
23
  import { makeSelectLocale } from '../../v2Containers/LanguageProvider/selectors';
24
24
  import CapSupportVideosWrapper from '@capillarytech/cap-ui-library/CapSupportVideosWrapper';
@@ -30,6 +30,7 @@ import {
30
30
  REQUEST,
31
31
  DEFAULT,
32
32
  ENABLE_PRODUCT_SUPPORT_VIDEOS,
33
+ CAMPAIGN_SETTINGS_URL,
33
34
  } from './constants';
34
35
  import './_cap.scss';
35
36
  import { Switch } from 'react-router';
@@ -528,6 +529,7 @@ export class Cap extends React.Component { // eslint-disable-line react/prefer-s
528
529
  changeOrg={this.changeOrg}
529
530
  isCreativesAccessible={isCreativesAccessible}
530
531
  logout={this.logout}
532
+ settingsUrl={`${engagePlusPublicPath}${CAMPAIGN_SETTINGS_URL}`}
531
533
  orgSettingsUrl={ORG_SETTINGS_URL}
532
534
  loggedIn={isLoggedIn}
533
535
  topbarMenuData={topbarMenuDataOptions}
@@ -20,6 +20,7 @@ import CapButton from '@capillarytech/cap-ui-library/CapButton';
20
20
  // import injectSaga from '../../utils/injectSaga'; // cap-coupons flows disabled
21
21
  // import injectReducer from '../../utils/injectReducer';
22
22
  import { makeSelectAuthenticated } from '../Cap/selectors';
23
+ import { createCentralCommsMetaId, getCentralCommsMetaIds } from '../../services/api';
23
24
  import DynamicControlsStep from './steps/DynamicControlsStep';
24
25
  import MessageTypeStep from './steps/MessageTypeStep';
25
26
  import CommunicationStrategyStep from './steps/CommunicationStrategyStep';
@@ -28,6 +29,15 @@ import {
28
29
  STEPS,
29
30
  CHANNEL_PRIORITY,
30
31
  AB_TEST,
32
+ DEFAULT_COMMUNICATION_STRATEGY_OPTIONS,
33
+ CHANNELS_WITHOUT_DELIVERY,
34
+ MESSAGE_TYPES_OPTIONS,
35
+ CHANNELS,
36
+ INCENTIVE_TYPES,
37
+ DYNAMIC_CONTROLS_CONFIG,
38
+ CHANNEL_CONTENT_KEY_MAP,
39
+ CHANNEL_DELIVERY_KEY_MAP,
40
+ CAMPAIGNS,
31
41
  } from './constants';
32
42
  import { getEnabledSteps } from './utils/getEnabledSteps';
33
43
  import messages from './messages';
@@ -43,6 +53,17 @@ import './CommunicationFlow.scss';
43
53
  // saga: CouponsCapContainer.couponsCapSaga,
44
54
  // });
45
55
 
56
+ const getDeliveryChannels = (contentItems) => {
57
+ const channels = contentItems.map((item) => (item.channel || '').toUpperCase()).filter(Boolean);
58
+ const unique = [...new Set(channels)];
59
+ return unique.filter((channel) => !CHANNELS_WITHOUT_DELIVERY.includes(channel));
60
+ };
61
+
62
+ const hasDeliverySettingForChannel = (channelSetting, channel) => {
63
+ const settings = channelSetting[channel];
64
+ return !!settings && Object.values(settings).some((v) => v !== null && v !== '' && v !== undefined);
65
+ };
66
+
46
67
  const CommunicationFlow = ({
47
68
  config,
48
69
  initialData,
@@ -50,13 +71,14 @@ const CommunicationFlow = ({
50
71
  onCancel, // eslint-disable-line
51
72
  onChange,
52
73
  intl,
53
- capData, // From Redux - contains user/org info needed by CouponsWrapper
74
+ cap, // From parent consumer (e.g. campaigns passes campaignCap) takes priority over Redux capData
75
+ capData, // From Redux - fallback when cap is not provided by consumer
54
76
  }) => {
55
77
  const { formatMessage } = intl || {};
56
78
  const { messageTypeData = {}, communicationStrategyData = {}, contentTemplateData = {} } = config?.features || {};
57
79
  // Initialize step data from initialData or defaults
58
80
  const [stepData, setStepData] = useState(() => {
59
- const defaultMessageType = messageTypeData.defaultOption?.value || null;
81
+ const defaultMessageType = messageTypeData.defaultOption?.value || MESSAGE_TYPES_OPTIONS?.[1]?.value || null;
60
82
  return {
61
83
  messageType: initialData?.messageType || defaultMessageType,
62
84
  communicationStrategy: initialData?.communicationStrategy || null,
@@ -73,6 +95,27 @@ const CommunicationFlow = ({
73
95
  // Memoize enabled steps
74
96
  const enabledSteps = useMemo(() => getEnabledSteps(config), [config]);
75
97
 
98
+ const isSaveDisabled = useMemo(() => {
99
+ const strategyRequired = enabledSteps.includes(STEPS.COMMUNICATION_STRATEGY);
100
+ const templateRequired = enabledSteps.includes(STEPS.CHANNEL_SELECTION);
101
+ const isMultiChannel = [CHANNEL_PRIORITY, AB_TEST].includes(stepData.communicationStrategy);
102
+
103
+ if (strategyRequired && !stepData.communicationStrategy) return true;
104
+ if (templateRequired && !isMultiChannel && (!stepData.contentItems || stepData.contentItems.length === 0)) return true;
105
+
106
+ const deliverySettingsEnabled = !!config?.features?.deliverySettingsData;
107
+ if (deliverySettingsEnabled && templateRequired) {
108
+ const deliveryChannels = getDeliveryChannels(stepData.contentItems || []);
109
+ const channelSetting = stepData.deliverySetting?.channelSetting || {};
110
+ const anyChannelMissingSenderDetails = deliveryChannels.some(
111
+ (channel) => !hasDeliverySettingForChannel(channelSetting, channel),
112
+ );
113
+ if (anyChannelMissingSenderDetails) return true;
114
+ }
115
+
116
+ return false;
117
+ }, [enabledSteps, stepData.communicationStrategy, stepData.contentItems, stepData.deliverySetting, config?.features?.deliverySettingsData]);
118
+
76
119
  /**
77
120
  * Get aggregated data from all steps
78
121
  */
@@ -106,6 +149,77 @@ const CommunicationFlow = ({
106
149
  }));
107
150
  }, []);
108
151
 
152
+ const handleSave = useCallback(async () => {
153
+ const aggregatedData = getAggregatedData();
154
+ const shouldUseCCS = config?.useCCS !== false;
155
+
156
+ if (shouldUseCCS) {
157
+ const ouId = config?.context?.ouId || -1;
158
+ const module = config?.context?.module
159
+ || (config?.consumer ? config.consumer.toUpperCase() : CAMPAIGNS);
160
+
161
+ const channelContentKeyMap = CHANNEL_CONTENT_KEY_MAP;
162
+ const channelDeliveryKeyMap = CHANNEL_DELIVERY_KEY_MAP;
163
+
164
+ const contentItems = aggregatedData.contentItems || [];
165
+ const { dynamicControls = {} } = aggregatedData;
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) {
175
+ try {
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);
213
+ }
214
+ } catch (error) {
215
+ console.error('[CommunicationFlow] CCS createCentralCommsMetaId error:', error);
216
+ }
217
+ }
218
+ }
219
+
220
+ onSave(aggregatedData);
221
+ }, [getAggregatedData, config, onSave]);
222
+
109
223
  // Call onChange callback when stepData changes
110
224
  useEffect(() => {
111
225
  if (onChange) {
@@ -131,8 +245,8 @@ const CommunicationFlow = ({
131
245
  <MessageTypeStep
132
246
  {...commonProps}
133
247
  value={stepData.messageType}
134
- options={messageTypeData.options}
135
- defaultOption={messageTypeData.defaultOption}
248
+ options={messageTypeData.options || MESSAGE_TYPES_OPTIONS}
249
+ defaultOption={messageTypeData.defaultOption || MESSAGE_TYPES_OPTIONS?.[1]}
136
250
  onChange={(messageType) => handleStepChange(step, { messageType })}
137
251
  />
138
252
  <CapDivider />
@@ -145,7 +259,7 @@ const CommunicationFlow = ({
145
259
  {...commonProps}
146
260
  value={stepData.communicationStrategy}
147
261
  // messageType={stepData.messageType}
148
- options={communicationStrategyData.options}
262
+ options={communicationStrategyData.options || DEFAULT_COMMUNICATION_STRATEGY_OPTIONS}
149
263
  disabled={communicationStrategyData.disabled}
150
264
  onChange={(communicationStrategy) => handleStepChange(step, { communicationStrategy })}
151
265
  />
@@ -162,30 +276,23 @@ const CommunicationFlow = ({
162
276
  <ChannelSelectionStep
163
277
  {...commonProps}
164
278
  value={stepData}
165
- channels={contentTemplateData.channels}
279
+ channels={contentTemplateData.channels || CHANNELS}
166
280
  onChange={(data) => handleStepChange(step, data)}
167
281
  channelsToHide={contentTemplateData.channelsToHide}
168
282
  channelsToDisable={contentTemplateData.channelsToDisable}
169
283
  creativesMode={config.mode || 'create'}
170
284
  selectedOfferDetails={stepData.selectedOfferDetails}
171
- incentivesData={config.features?.incentivesData}
285
+ incentivesData={{
286
+ ...config.features?.incentivesData,
287
+ types: config.features?.incentivesData?.types || INCENTIVE_TYPES,
288
+ }}
172
289
  deliverySettingsData={config.features?.deliverySettingsData}
173
290
  config={config}
174
- capData={capData}
291
+ capData={cap || capData}
175
292
  />
176
293
  <CapDivider />
177
294
  </CapRow>
178
295
  );
179
- // This will be added back in when coupons/points are integrated so keeping it commented out for now
180
- // case STEPS.INCENTIVES:
181
- // return (
182
- // <IncentivesStep
183
- // key={step}
184
- // {...commonProps}
185
- // value={stepData.selectedOfferDetails}
186
- // onChange={(selectedOfferDetails) => handleStepChange(step, { selectedOfferDetails })}
187
- // />
188
- // );
189
296
  case STEPS.DYNAMIC_CONTROLS:
190
297
  // Only show DynamicControlsStep if communication strategy is selected
191
298
  if (!stepData.communicationStrategy) {
@@ -196,7 +303,7 @@ const CommunicationFlow = ({
196
303
  key={step}
197
304
  {...commonProps}
198
305
  value={{ dynamicControls: stepData.dynamicControls }}
199
- controls={config.features?.dynamicControlsData?.controls || []}
306
+ controls={config.features?.dynamicControlsData?.controls || DYNAMIC_CONTROLS_CONFIG}
200
307
  onChange={(data) => handleStepChange(step, { dynamicControls: data.dynamicControls })}
201
308
  />
202
309
  );
@@ -210,7 +317,7 @@ const CommunicationFlow = ({
210
317
  {renderSteps()}
211
318
  {onSave && (
212
319
  <CapRow useLegacy className="communication-flow-container__footer">
213
- <CapButton type="primary" onClick={() => onSave(getAggregatedData())}>
320
+ <CapButton type="primary" onClick={handleSave} disabled={isSaveDisabled}>
214
321
  {formatMessage(messages.save)}
215
322
  </CapButton>
216
323
  </CapRow>
@@ -262,18 +369,21 @@ CommunicationFlow.propTypes = {
262
369
  incentivesTypes: PropTypes.arrayOf(PropTypes.oneOf(['coupons', 'points', 'promotions', 'giftVouchers', 'badges'])),
263
370
  }),
264
371
  context: PropTypes.object,
372
+ useCCS: PropTypes.bool,
265
373
  }).isRequired,
266
374
  initialData: PropTypes.object,
267
375
  onSave: PropTypes.func.isRequired,
268
376
  onCancel: PropTypes.func.isRequired,
269
377
  onChange: PropTypes.func,
270
378
  intl: PropTypes.object.isRequired,
379
+ cap: PropTypes.object,
271
380
  capData: PropTypes.object,
272
381
  };
273
382
 
274
383
  CommunicationFlow.defaultProps = {
275
384
  initialData: null,
276
385
  onChange: null,
386
+ cap: null,
277
387
  capData: {},
278
388
  };
279
389
 
@@ -10,6 +10,123 @@
10
10
  }
11
11
  }
12
12
 
13
+ // Mobilepush preview styles for CommunicationFlowCard (CapCard context, no .MOBILEPUSH wrapper)
14
+ .communication-flow-summary-card {
15
+ border-color: $CAP_G12;
16
+ width: 40rem;
17
+
18
+ .summary-card-title-row {
19
+ gap: 0.25rem;
20
+ }
21
+
22
+ .card-body-row {
23
+ height: 10.125rem;
24
+ display: flex;
25
+ }
26
+
27
+ .card-body-left-col {
28
+ padding: 1rem;
29
+ border-right: 0.0625rem solid $CAP_G12;
30
+ height: 100%;
31
+ overflow: auto;
32
+
33
+ .channel-header-row {
34
+ gap: 0.5rem;
35
+ margin-bottom: 0.5rem;
36
+ }
37
+ }
38
+
39
+ .card-body-right-col {
40
+ padding: 1rem;
41
+ height: 100%;
42
+ overflow: auto;
43
+
44
+ .control-row {
45
+ margin-top: 0.5rem;
46
+ }
47
+ }
48
+
49
+ .mobilepush-container {
50
+ background-color: $CAP_WHITE;
51
+ padding: $CAP_SPACE_12;
52
+ .app-header {
53
+ color: #5D5D5D;
54
+ font-weight: 600;
55
+ padding: 0.285rem 0.571rem;
56
+ display: flex;
57
+ align-items: center;
58
+ justify-content: space-between;
59
+ .app-header-left {
60
+ display: flex;
61
+ align-items: center;
62
+ div {
63
+ overflow-wrap: break-word;
64
+ }
65
+ .app-icon {
66
+ width: 0.857rem;
67
+ height: 0.857rem;
68
+ border-radius: 50%;
69
+ background-color: #737070;
70
+ margin-right: $CAP_SPACE_06;
71
+ }
72
+ }
73
+ }
74
+ .mobilepush-message {
75
+ padding-left: 1.65rem;
76
+ }
77
+ .mobilepush-image {
78
+ width: 100%;
79
+ height: 7.5rem;
80
+ margin-top: 0.625rem;
81
+ }
82
+ .mobilepush-image-padding {
83
+ padding-left: 1.65rem;
84
+ }
85
+ .scroll-container {
86
+ overflow-x: auto;
87
+ display: flex;
88
+ padding-top: $CAP_SPACE_06;
89
+ padding-right: $CAP_SPACE_06;
90
+ white-space: nowrap;
91
+ scrollbar-width: none;
92
+ &::-webkit-scrollbar {
93
+ display: none;
94
+ }
95
+ height: 100%;
96
+ width: 100%;
97
+ margin-left: 1.65rem;
98
+ .whatsapp-carousel-container {
99
+ padding: $CAP_SPACE_04 0 $CAP_SPACE_08;
100
+ border-radius: $CAP_SPACE_06;
101
+ background-color: $CAP_WHITE;
102
+ width: 80%;
103
+ flex-shrink: 0;
104
+ white-space: pre-wrap;
105
+ overflow-wrap: break-word;
106
+ .whatsapp-carousel-card {
107
+ margin: $CAP_SPACE_02;
108
+ }
109
+ }
110
+ }
111
+ .actions {
112
+ background-color: $CAP_WHITE;
113
+ height: auto;
114
+ padding: $CAP_SPACE_08;
115
+ text-align: center;
116
+ display: flex;
117
+ flex-direction: column;
118
+ align-items: center;
119
+ justify-content: center;
120
+ gap: $CAP_SPACE_08;
121
+ .action {
122
+ font-weight: 600;
123
+ color: #1970DA;
124
+ height: 1.25rem;
125
+ }
126
+ }
127
+ }
128
+ }
129
+
13
130
  .communication-flow-container {
14
131
  .step-divider {
15
132
  margin: $CAP_SPACE_32 0;
@@ -23,3 +140,40 @@
23
140
  padding: $CAP_SPACE_16 0 $CAP_SPACE_08;
24
141
  }
25
142
  }
143
+
144
+ .communication-flow-card-empty {
145
+ width: 30rem;
146
+ border-color: $CAP_G12;
147
+
148
+ .empty-card-body-row {
149
+ height: 100%;
150
+ }
151
+
152
+ .empty-card-illustration-col {
153
+ display: flex;
154
+ align-items: center;
155
+ justify-content: center;
156
+ height: 100%;
157
+ }
158
+
159
+ .empty-card-action-col {
160
+ padding: 1.5rem 1rem;
161
+ height: 100%;
162
+ display: flex;
163
+ align-items: center;
164
+ }
165
+ }
166
+
167
+ .slide-box-wrapper {
168
+ .cap-slide-box-v2-container {
169
+ .slidebox-header,
170
+ .slidebox-content-container,
171
+ .slidebox-footer {
172
+ margin-bottom: 0;
173
+ padding: 0;
174
+ &.has-footer {
175
+ overflow-x: hidden;
176
+ }
177
+ }
178
+ }
179
+ }