@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.
- package/constants/unified.js +1 -0
- package/package.json +1 -1
- package/services/api.js +6 -0
- package/services/tests/api.test.js +7 -0
- package/utils/common.js +6 -1
- package/utils/tests/tagValidations.test.js +34 -0
- package/v2Components/CapTagList/index.js +15 -22
- package/v2Components/CapTagList/style.scss +48 -0
- package/v2Components/CapTagListWithInput/__tests__/CapTagListWithInput.test.js +63 -0
- package/v2Components/CapTagListWithInput/index.js +4 -0
- package/v2Components/CapWhatsappCTA/index.js +2 -0
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +180 -0
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +96 -0
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +99 -0
- package/v2Components/CommonTestAndPreview/tests/index.test.js +113 -3
- package/v2Components/FormBuilder/index.js +7 -0
- package/v2Components/HtmlEditor/HTMLEditor.js +6 -1
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -0
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +927 -2
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +3 -0
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +95 -0
- package/v2Containers/BeeEditor/index.js +3 -0
- package/v2Containers/CommunicationFlow/CommunicationFlow.js +291 -0
- package/v2Containers/CommunicationFlow/CommunicationFlow.scss +25 -0
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +255 -0
- package/v2Containers/CommunicationFlow/constants.js +200 -0
- package/v2Containers/CommunicationFlow/index.js +102 -0
- package/v2Containers/CommunicationFlow/messages.js +346 -0
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.js +522 -0
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.scss +170 -0
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +796 -0
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/index.js +5 -0
- package/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/CommunicationStrategyStep.js +95 -0
- package/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/Tests/CommunicationStrategyStep.test.js +133 -0
- package/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/index.js +5 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.js +289 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.scss +70 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.js +319 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.scss +69 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +616 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +577 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/deliverySettingsConfig.test.js +1111 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/deliverySettingsConfig.js +696 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/index.js +7 -0
- package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.js +102 -0
- package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.scss +36 -0
- package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/Tests/DynamicControlsStep.test.js +91 -0
- package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/index.js +5 -0
- package/v2Containers/CommunicationFlow/steps/MessageTypeStep/MessageTypeStep.js +86 -0
- package/v2Containers/CommunicationFlow/steps/MessageTypeStep/Tests/MessageTypeStep.test.js +100 -0
- package/v2Containers/CommunicationFlow/steps/MessageTypeStep/index.js +5 -0
- package/v2Containers/CommunicationFlow/utils/getEnabledSteps.js +30 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +28 -1
- package/v2Containers/CreativesContainer/constants.js +3 -0
- package/v2Containers/CreativesContainer/index.js +3 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +104 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +110 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +363 -0
- package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
- package/v2Containers/Email/index.js +1 -0
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +7 -1
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +3 -0
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +20 -2
- package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +16 -1
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +3 -0
- package/v2Containers/EmailWrapper/index.js +4 -0
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +1 -0
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +9 -0
- package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +19 -0
- package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +3 -0
- package/v2Containers/InAppWrapper/index.js +3 -0
- package/v2Containers/MobilePush/Create/index.js +2 -0
- package/v2Containers/MobilePush/Edit/index.js +2 -0
- package/v2Containers/MobilepushWrapper/index.js +3 -1
- package/v2Containers/Rcs/index.js +1 -0
- package/v2Containers/Sms/Create/index.js +2 -0
- package/v2Containers/Sms/Edit/index.js +2 -0
- package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
- package/v2Containers/SmsTrai/Edit/index.js +2 -0
- package/v2Containers/SmsWrapper/index.js +2 -0
- package/v2Containers/TagList/index.js +41 -2
- package/v2Containers/TagList/messages.js +4 -0
- package/v2Containers/TagList/tests/TagList.test.js +122 -20
- package/v2Containers/TagList/tests/mockdata.js +17 -0
- package/v2Containers/Templates/tests/sagas.test.js +83 -0
- package/v2Containers/Viber/index.js +5 -0
- package/v2Containers/WebPush/Create/hooks/useTagManagement.js +0 -2
- package/v2Containers/WebPush/Create/index.js +9 -1
- package/v2Containers/Whatsapp/index.js +5 -0
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +20 -0
- 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
|
+
}
|