@capillarytech/creatives-library 8.0.316-alpha.4 → 8.0.317-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.
Files changed (91) hide show
  1. package/constants/unified.js +1 -0
  2. package/package.json +1 -1
  3. package/services/api.js +6 -0
  4. package/services/tests/api.test.js +7 -0
  5. package/utils/common.js +6 -1
  6. package/utils/tests/tagValidations.test.js +34 -0
  7. package/v2Components/CapTagList/index.js +15 -22
  8. package/v2Components/CapTagList/style.scss +48 -0
  9. package/v2Components/CapTagListWithInput/__tests__/CapTagListWithInput.test.js +63 -0
  10. package/v2Components/CapTagListWithInput/index.js +4 -0
  11. package/v2Components/CapWhatsappCTA/index.js +2 -0
  12. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +180 -0
  13. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +96 -0
  14. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +99 -0
  15. package/v2Components/CommonTestAndPreview/tests/index.test.js +113 -3
  16. package/v2Components/FormBuilder/index.js +7 -0
  17. package/v2Components/HtmlEditor/HTMLEditor.js +6 -1
  18. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -0
  19. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +927 -2
  20. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +3 -0
  21. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +95 -0
  22. package/v2Containers/BeeEditor/index.js +3 -0
  23. package/v2Containers/CommunicationFlow/CommunicationFlow.js +291 -0
  24. package/v2Containers/CommunicationFlow/CommunicationFlow.scss +25 -0
  25. package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +255 -0
  26. package/v2Containers/CommunicationFlow/constants.js +200 -0
  27. package/v2Containers/CommunicationFlow/index.js +102 -0
  28. package/v2Containers/CommunicationFlow/messages.js +346 -0
  29. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.js +522 -0
  30. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.scss +170 -0
  31. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +796 -0
  32. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/index.js +5 -0
  33. package/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/CommunicationStrategyStep.js +95 -0
  34. package/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/Tests/CommunicationStrategyStep.test.js +133 -0
  35. package/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/index.js +5 -0
  36. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.js +289 -0
  37. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.scss +70 -0
  38. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.js +319 -0
  39. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.scss +69 -0
  40. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +616 -0
  41. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +577 -0
  42. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/deliverySettingsConfig.test.js +1111 -0
  43. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/deliverySettingsConfig.js +696 -0
  44. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/index.js +7 -0
  45. package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.js +102 -0
  46. package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.scss +36 -0
  47. package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/Tests/DynamicControlsStep.test.js +91 -0
  48. package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/index.js +5 -0
  49. package/v2Containers/CommunicationFlow/steps/MessageTypeStep/MessageTypeStep.js +86 -0
  50. package/v2Containers/CommunicationFlow/steps/MessageTypeStep/Tests/MessageTypeStep.test.js +100 -0
  51. package/v2Containers/CommunicationFlow/steps/MessageTypeStep/index.js +5 -0
  52. package/v2Containers/CommunicationFlow/utils/getEnabledSteps.js +30 -0
  53. package/v2Containers/CreativesContainer/SlideBoxContent.js +28 -1
  54. package/v2Containers/CreativesContainer/constants.js +3 -0
  55. package/v2Containers/CreativesContainer/index.js +3 -0
  56. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +104 -0
  57. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +110 -0
  58. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +363 -0
  59. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
  60. package/v2Containers/Email/index.js +1 -0
  61. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +7 -1
  62. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +3 -0
  63. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +20 -2
  64. package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +16 -1
  65. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +3 -0
  66. package/v2Containers/EmailWrapper/index.js +4 -0
  67. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +1 -0
  68. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +9 -0
  69. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +19 -0
  70. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +3 -0
  71. package/v2Containers/InAppWrapper/index.js +3 -0
  72. package/v2Containers/MobilePush/Create/index.js +2 -0
  73. package/v2Containers/MobilePush/Edit/index.js +2 -0
  74. package/v2Containers/MobilepushWrapper/index.js +3 -1
  75. package/v2Containers/Rcs/index.js +1 -0
  76. package/v2Containers/Sms/Create/index.js +2 -0
  77. package/v2Containers/Sms/Edit/index.js +2 -0
  78. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  79. package/v2Containers/SmsTrai/Edit/index.js +2 -0
  80. package/v2Containers/SmsWrapper/index.js +2 -0
  81. package/v2Containers/TagList/index.js +41 -2
  82. package/v2Containers/TagList/messages.js +4 -0
  83. package/v2Containers/TagList/tests/TagList.test.js +122 -20
  84. package/v2Containers/TagList/tests/mockdata.js +17 -0
  85. package/v2Containers/Templates/tests/sagas.test.js +83 -0
  86. package/v2Containers/Viber/index.js +5 -0
  87. package/v2Containers/WebPush/Create/hooks/useTagManagement.js +0 -2
  88. package/v2Containers/WebPush/Create/index.js +9 -1
  89. package/v2Containers/Whatsapp/index.js +5 -0
  90. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +20 -0
  91. package/v2Containers/Zalo/index.js +2 -0
@@ -59,6 +59,7 @@ const CodeEditorPaneComponent = ({
59
59
  injectedTags = {},
60
60
  location,
61
61
  eventContextTags = [],
62
+ waitEventContextTags = {},
62
63
  selectedOfferDetails = [],
63
64
  channel,
64
65
  userLocale = 'en',
@@ -289,6 +290,7 @@ const CodeEditorPaneComponent = ({
289
290
  location={location}
290
291
  selectedOfferDetails={selectedOfferDetails}
291
292
  eventContextTags={eventContextTags}
293
+ waitEventContextTags={waitEventContextTags}
292
294
  popoverPlacement="rightTop"
293
295
  />
294
296
  </CapRow>
@@ -327,6 +329,7 @@ CodeEditorPane.propTypes = {
327
329
  injectedTags: PropTypes.object,
328
330
  location: PropTypes.object,
329
331
  eventContextTags: PropTypes.array,
332
+ waitEventContextTags: PropTypes.object,
330
333
  selectedOfferDetails: PropTypes.array,
331
334
  channel: PropTypes.string,
332
335
  userLocale: PropTypes.string,
@@ -284,6 +284,101 @@ describe('useLocalTemplateList', () => {
284
284
  expect(fetchTemplates.mock.calls.length).toBe(callsAfterFirst);
285
285
  });
286
286
 
287
+ it('passes explicit search term from reset() through to fetchTemplates', async () => {
288
+ const fetchTemplates = jest.fn().mockResolvedValue({
289
+ templates: [{ _id: 'r1' }],
290
+ totalCount: 5,
291
+ });
292
+
293
+ const { result } = renderHook(() =>
294
+ useLocalTemplateList({ fetchTemplates, perPage: 25 }),
295
+ );
296
+
297
+ act(() => {
298
+ result.current.reset('explicit-search');
299
+ });
300
+
301
+ await waitFor(() => {
302
+ expect(result.current.loading).toBe(false);
303
+ });
304
+
305
+ expect(fetchTemplates).toHaveBeenCalledWith(
306
+ expect.objectContaining({ search: 'explicit-search', page: 1, reset: true }),
307
+ );
308
+ expect(result.current.search).toBe('explicit-search');
309
+ });
310
+
311
+ it('stores a positive totalCount returned by the API', async () => {
312
+ const fetchTemplates = jest.fn().mockResolvedValue({
313
+ templates: [{ _id: 'a' }, { _id: 'b' }],
314
+ totalCount: 42,
315
+ });
316
+
317
+ const { result } = renderHook(() =>
318
+ useLocalTemplateList({ fetchTemplates, perPage: 25 }),
319
+ );
320
+
321
+ act(() => {
322
+ result.current.reset('');
323
+ });
324
+
325
+ await waitFor(() => {
326
+ expect(result.current.loading).toBe(false);
327
+ });
328
+
329
+ expect(result.current.totalCount).toBe(42);
330
+ expect(result.current.templates).toHaveLength(2);
331
+ });
332
+
333
+ it('canLoadMore is false while a fetch is in progress even when more items exist', async () => {
334
+ let resolveFirst;
335
+ const fetchTemplates = jest.fn(
336
+ () => new Promise((res) => { resolveFirst = res; }),
337
+ );
338
+
339
+ const { result } = renderHook(() =>
340
+ useLocalTemplateList({ fetchTemplates, perPage: 2 }),
341
+ );
342
+
343
+ act(() => {
344
+ result.current.reset('');
345
+ });
346
+
347
+ // Loading is true → canLoadMore must be false regardless of hasMoreByTotal
348
+ expect(result.current.loading).toBe(true);
349
+ expect(result.current.canLoadMore).toBe(false);
350
+
351
+ await act(async () => {
352
+ resolveFirst({ templates: [{ _id: 'x' }, { _id: 'y' }], totalCount: 10 });
353
+ await Promise.resolve();
354
+ });
355
+
356
+ expect(result.current.loading).toBe(false);
357
+ expect(result.current.canLoadMore).toBe(true);
358
+ });
359
+
360
+ it('canLoadMore is false when templates is empty (hasMoreByFullPage requires length > 0)', async () => {
361
+ const fetchTemplates = jest.fn().mockResolvedValue({
362
+ templates: [],
363
+ totalCount: 0,
364
+ });
365
+
366
+ const { result } = renderHook(() =>
367
+ useLocalTemplateList({ fetchTemplates, perPage: 0 }),
368
+ );
369
+
370
+ act(() => {
371
+ result.current.reset('');
372
+ });
373
+
374
+ await waitFor(() => {
375
+ expect(result.current.loading).toBe(false);
376
+ });
377
+
378
+ // templates.length === 0 means hasMoreByFullPage is false even if lastFetchFullPage would be true
379
+ expect(result.current.canLoadMore).toBe(false);
380
+ });
381
+
287
382
  it('allows loadMore when totalCount is unknown but first page is full (hasMoreByFullPage)', async () => {
288
383
  const fetchTemplates = jest.fn(({ page }) => {
289
384
  if (page === 1) {
@@ -53,6 +53,7 @@ function BeeEditor(props) {
53
53
  BEESelect,
54
54
  currentOrgDetails,
55
55
  eventContextTags,
56
+ waitEventContextTags,
56
57
  } = props;
57
58
  const { saveRowRequest } = BEESelect;
58
59
  const {formatMessage} = intl;
@@ -368,6 +369,7 @@ function BeeEditor(props) {
368
369
  }}
369
370
  onContextChange={onContextChange}
370
371
  eventContextTags={eventContextTags}
372
+ waitEventContextTags={waitEventContextTags}
371
373
  />
372
374
  <CapModal
373
375
  className="custom-row-modal"
@@ -414,6 +416,7 @@ BeeEditor.propTypes = {
414
416
  onContextChange: PropTypes.func,
415
417
  userLocale: PropTypes.string,
416
418
  eventContextTags: PropTypes.array,
419
+ waitEventContextTags: PropTypes.object,
417
420
  };
418
421
 
419
422
  const mapStateToProps = () => createStructuredSelector({
@@ -0,0 +1,291 @@
1
+ /**
2
+ * CommunicationFlow - Orchestrator Component
3
+ *
4
+ * Manages the step flow, validation, and data aggregation.
5
+ * Renders steps based on config and calls callbacks with aggregated data.
6
+ */
7
+
8
+ import React, {
9
+ useState, useCallback, useMemo, useEffect,
10
+ } from 'react';
11
+ import PropTypes from 'prop-types';
12
+ import { injectIntl } from 'react-intl';
13
+ import { compose } from 'redux';
14
+ import { connect } from 'react-redux';
15
+ import { createStructuredSelector } from 'reselect';
16
+ // import { CouponsCapContainer } from '@capillarytech/cap-coupons'; // Commented - cap-coupons flows disabled
17
+ import CapRow from '@capillarytech/cap-ui-library/CapRow';
18
+ import CapDivider from '@capillarytech/cap-ui-library/CapDivider';
19
+ import CapButton from '@capillarytech/cap-ui-library/CapButton';
20
+ // import injectSaga from '../../utils/injectSaga'; // cap-coupons flows disabled
21
+ // import injectReducer from '../../utils/injectReducer';
22
+ import { makeSelectAuthenticated } from '../Cap/selectors';
23
+ import DynamicControlsStep from './steps/DynamicControlsStep';
24
+ import MessageTypeStep from './steps/MessageTypeStep';
25
+ import CommunicationStrategyStep from './steps/CommunicationStrategyStep';
26
+ import ChannelSelectionStep from './steps/ChannelSelectionStep';
27
+ import {
28
+ STEPS,
29
+ CHANNEL_PRIORITY,
30
+ AB_TEST,
31
+ } from './constants';
32
+ import { getEnabledSteps } from './utils/getEnabledSteps';
33
+ import messages from './messages';
34
+ import './CommunicationFlow.scss';
35
+
36
+ // Inject couponsCap reducer and saga - commented out (cap-coupons flows disabled)
37
+ // const withCouponsReducer = injectReducer({
38
+ // key: 'couponsCap',
39
+ // reducer: CouponsCapContainer.couponsCapReducer,
40
+ // });
41
+ // const withCouponsSaga = injectSaga({
42
+ // key: 'couponsCapSaga',
43
+ // saga: CouponsCapContainer.couponsCapSaga,
44
+ // });
45
+
46
+ const CommunicationFlow = ({
47
+ config,
48
+ initialData,
49
+ onSave,
50
+ onCancel, // eslint-disable-line
51
+ onChange,
52
+ intl,
53
+ capData, // From Redux - contains user/org info needed by CouponsWrapper
54
+ }) => {
55
+ const { formatMessage } = intl || {};
56
+ const { messageTypeData = {}, communicationStrategyData = {}, contentTemplateData = {} } = config?.features || {};
57
+ // Initialize step data from initialData or defaults
58
+ const [stepData, setStepData] = useState(() => {
59
+ const defaultMessageType = messageTypeData.defaultOption?.value || null;
60
+ return {
61
+ messageType: initialData?.messageType || defaultMessageType,
62
+ communicationStrategy: initialData?.communicationStrategy || null,
63
+ channel: initialData?.channel || config.channel || null,
64
+ channels: initialData?.channels || [],
65
+ selectedOfferDetails: initialData?.selectedOfferDetails || [],
66
+ contentItems: initialData?.contentItems || [],
67
+ deliverySetting: initialData?.deliverySetting || {},
68
+ dynamicControls: initialData?.dynamicControls || {},
69
+ };
70
+ });
71
+ const [validationErrors, setValidationErrors] = useState({});
72
+
73
+ // Memoize enabled steps
74
+ const enabledSteps = useMemo(() => getEnabledSteps(config), [config]);
75
+
76
+ /**
77
+ * Get aggregated data from all steps
78
+ */
79
+ const getAggregatedData = useCallback(() => {
80
+ const { communicationStrategy } = stepData;
81
+ const isMultiChannel = [CHANNEL_PRIORITY, AB_TEST].includes(communicationStrategy);
82
+
83
+ return {
84
+ messageType: stepData.messageType,
85
+ communicationStrategy: stepData.communicationStrategy,
86
+ channel: isMultiChannel ? null : stepData.channel,
87
+ channels: isMultiChannel ? stepData.channels : [],
88
+ selectedOfferDetails: stepData.selectedOfferDetails,
89
+ contentItems: stepData.contentItems || [],
90
+ deliverySetting: stepData.deliverySetting,
91
+ dynamicControls: stepData.dynamicControls,
92
+ };
93
+ }, [stepData]);
94
+
95
+ /**
96
+ * Handle step data change
97
+ */
98
+ const handleStepChange = useCallback((step, data) => {
99
+ setStepData((prevStepData) => ({
100
+ ...prevStepData,
101
+ ...data,
102
+ }));
103
+ setValidationErrors((prevErrors) => ({
104
+ ...prevErrors,
105
+ [step]: null, // Clear validation error for this step
106
+ }));
107
+ }, []);
108
+
109
+ // Call onChange callback when stepData changes
110
+ useEffect(() => {
111
+ if (onChange) {
112
+ onChange(getAggregatedData());
113
+ }
114
+ // eslint-disable-next-line react-hooks/exhaustive-deps
115
+ }, [stepData]);
116
+
117
+ /**
118
+ * Render all enabled steps
119
+ */
120
+ const renderSteps = useCallback(() => enabledSteps.map((step) => {
121
+ const commonProps = {
122
+ value: stepData,
123
+ onChange: (data) => handleStepChange(step, data),
124
+ error: validationErrors[step],
125
+ };
126
+
127
+ switch (step) {
128
+ case STEPS.MESSAGE_TYPE:
129
+ return (
130
+ <CapRow key={step}>
131
+ <MessageTypeStep
132
+ {...commonProps}
133
+ value={stepData.messageType}
134
+ options={messageTypeData.options}
135
+ defaultOption={messageTypeData.defaultOption}
136
+ onChange={(messageType) => handleStepChange(step, { messageType })}
137
+ />
138
+ <CapDivider />
139
+ </CapRow>
140
+ );
141
+ case STEPS.COMMUNICATION_STRATEGY:
142
+ return (
143
+ <CapRow key={step}>
144
+ <CommunicationStrategyStep
145
+ {...commonProps}
146
+ value={stepData.communicationStrategy}
147
+ // messageType={stepData.messageType}
148
+ options={communicationStrategyData.options}
149
+ disabled={communicationStrategyData.disabled}
150
+ onChange={(communicationStrategy) => handleStepChange(step, { communicationStrategy })}
151
+ />
152
+ <CapDivider />
153
+ </CapRow>
154
+ );
155
+ case STEPS.CHANNEL_SELECTION:
156
+ // Only show ChannelSelectionStep if communication strategy is selected
157
+ if (!stepData.communicationStrategy) {
158
+ return null;
159
+ }
160
+ return (
161
+ <CapRow key={step}>
162
+ <ChannelSelectionStep
163
+ {...commonProps}
164
+ value={stepData}
165
+ channels={contentTemplateData.channels}
166
+ onChange={(data) => handleStepChange(step, data)}
167
+ channelsToHide={contentTemplateData.channelsToHide}
168
+ channelsToDisable={contentTemplateData.channelsToDisable}
169
+ creativesMode={config.mode || 'create'}
170
+ selectedOfferDetails={stepData.selectedOfferDetails}
171
+ incentivesData={config.features?.incentivesData}
172
+ deliverySettingsData={config.features?.deliverySettingsData}
173
+ config={config}
174
+ capData={capData}
175
+ />
176
+ <CapDivider />
177
+ </CapRow>
178
+ );
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
+ case STEPS.DYNAMIC_CONTROLS:
190
+ // Only show DynamicControlsStep if communication strategy is selected
191
+ if (!stepData.communicationStrategy) {
192
+ return null;
193
+ }
194
+ return (
195
+ <DynamicControlsStep
196
+ key={step}
197
+ {...commonProps}
198
+ value={{ dynamicControls: stepData.dynamicControls }}
199
+ controls={config.features?.dynamicControlsData?.controls || []}
200
+ onChange={(data) => handleStepChange(step, { dynamicControls: data.dynamicControls })}
201
+ />
202
+ );
203
+ default:
204
+ return null;
205
+ }
206
+ }), [enabledSteps, stepData, validationErrors, config, handleStepChange, messageTypeData, communicationStrategyData, contentTemplateData]);
207
+
208
+ return (
209
+ <CapRow className="communication-flow-container">
210
+ {renderSteps()}
211
+ {onSave && (
212
+ <CapRow className="communication-flow-container__footer">
213
+ <CapButton type="primary" onClick={() => onSave(getAggregatedData())}>
214
+ {formatMessage(messages.save)}
215
+ </CapButton>
216
+ </CapRow>
217
+ )}
218
+ </CapRow>
219
+ );
220
+ };
221
+
222
+ CommunicationFlow.propTypes = {
223
+ config: PropTypes.shape({
224
+ consumer: PropTypes.oneOf(['campaigns', 'loyalty', 'adiona']).isRequired,
225
+ channel: PropTypes.string,
226
+ mode: PropTypes.oneOf(['create', 'edit', 'preview']).isRequired,
227
+ channelsToHide: PropTypes.arrayOf(PropTypes.string),
228
+ channelsToDisable: PropTypes.arrayOf(PropTypes.string),
229
+ features: PropTypes.shape({
230
+ enableMessageType: PropTypes.bool,
231
+ enableCommunicationStrategy: PropTypes.bool,
232
+ messageTypeData: PropTypes.shape({
233
+ options: PropTypes.arrayOf(PropTypes.shape({
234
+ value: PropTypes.string.isRequired,
235
+ label: PropTypes.node.isRequired,
236
+ })),
237
+ defaultOption: PropTypes.shape({
238
+ value: PropTypes.string.isRequired,
239
+ label: PropTypes.node.isRequired,
240
+ }),
241
+ required: PropTypes.bool,
242
+ }),
243
+ communicationStrategyData: PropTypes.shape({
244
+ options: PropTypes.arrayOf(PropTypes.shape({
245
+ value: PropTypes.string.isRequired,
246
+ label: PropTypes.node.isRequired,
247
+ })),
248
+ defaultOption: PropTypes.shape({
249
+ value: PropTypes.string.isRequired,
250
+ label: PropTypes.node.isRequired,
251
+ }),
252
+ required: PropTypes.bool,
253
+ disabled: PropTypes.bool,
254
+ }),
255
+ contentTemplateData: PropTypes.shape({
256
+ required: PropTypes.bool,
257
+ channels: PropTypes.object,
258
+ }),
259
+ enableIncentives: PropTypes.bool,
260
+ enableDeliverySettings: PropTypes.bool,
261
+ enableOtherSettings: PropTypes.bool,
262
+ incentivesTypes: PropTypes.arrayOf(PropTypes.oneOf(['coupons', 'points', 'promotions', 'giftVouchers', 'badges'])),
263
+ }),
264
+ context: PropTypes.object,
265
+ }).isRequired,
266
+ initialData: PropTypes.object,
267
+ onSave: PropTypes.func.isRequired,
268
+ onCancel: PropTypes.func.isRequired,
269
+ onChange: PropTypes.func,
270
+ intl: PropTypes.object.isRequired,
271
+ capData: PropTypes.object,
272
+ };
273
+
274
+ CommunicationFlow.defaultProps = {
275
+ initialData: null,
276
+ onChange: null,
277
+ capData: {},
278
+ };
279
+
280
+ const mapStateToProps = createStructuredSelector({
281
+ capData: makeSelectAuthenticated(),
282
+ });
283
+
284
+ const withConnect = connect(mapStateToProps);
285
+
286
+ export default compose(
287
+ // withCouponsReducer, // cap-coupons flows disabled
288
+ // withCouponsSaga, // cap-coupons flows disabled
289
+ withConnect,
290
+ injectIntl,
291
+ )(CommunicationFlow);
@@ -0,0 +1,25 @@
1
+ @import '~@capillarytech/cap-ui-library/styles/_variables.scss';
2
+
3
+ // .slidebox-content-container is a DOM ancestor of .communication-flow-container
4
+ // (the outer CapSlideBox wraps this component), so it cannot be scoped as a
5
+ // descendant selector — it must remain at root level to match correctly.
6
+ .slidebox-content-container {
7
+ padding: 0 $CAP_SPACE_48 !important; // 48px left and right, 0 top and bottom
8
+ .slidebox-footer {
9
+ padding: 0 $CAP_SPACE_48 !important;
10
+ }
11
+ }
12
+
13
+ .communication-flow-container {
14
+ .step-divider {
15
+ margin: $CAP_SPACE_32 0;
16
+ }
17
+
18
+ .heading-style {
19
+ margin-bottom: $CAP_SPACE_08;
20
+ }
21
+
22
+ &__footer {
23
+ padding: $CAP_SPACE_16 0 $CAP_SPACE_08;
24
+ }
25
+ }