@capillarytech/creatives-library 9.0.56-alpha.7 → 9.0.56-betaa.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 (82) hide show
  1. package/constants/unified.js +4 -2
  2. package/package.json +1 -1
  3. package/services/api.js +0 -20
  4. package/utils/common.js +12 -0
  5. package/utils/templateVarUtils.js +0 -16
  6. package/utils/tests/templateVarUtils.test.js +0 -44
  7. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +0 -13
  8. package/v2Components/CommonTestAndPreview/actions.js +0 -13
  9. package/v2Components/CommonTestAndPreview/constants.js +0 -8
  10. package/v2Components/CommonTestAndPreview/index.js +3 -55
  11. package/v2Components/CommonTestAndPreview/reducer.js +0 -20
  12. package/v2Components/CommonTestAndPreview/sagas.js +0 -28
  13. package/v2Components/CommonTestAndPreview/selectors.js +0 -6
  14. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +0 -106
  15. package/v2Components/CommonTestAndPreview/tests/actions.test.js +0 -15
  16. package/v2Components/CommonTestAndPreview/tests/constants.test.js +1 -9
  17. package/v2Components/CommonTestAndPreview/tests/index.test.js +0 -101
  18. package/v2Components/CommonTestAndPreview/tests/reducer.test.js +0 -41
  19. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +0 -84
  20. package/v2Components/CommonTestAndPreview/tests/selectors.test.js +0 -12
  21. package/v2Components/FormBuilder/Functional/FormBuilderShell.js +320 -46
  22. package/v2Components/FormBuilder/Functional/channels/mobilepush/buildSubmitPayload.js +10 -0
  23. package/v2Components/FormBuilder/Functional/channels/mobilepush/config.js +82 -0
  24. package/v2Components/FormBuilder/Functional/channels/mobilepush/getEditorErrorDescriptor.js +74 -0
  25. package/v2Components/FormBuilder/Functional/channels/mobilepush/index.js +28 -0
  26. package/v2Components/FormBuilder/Functional/channels/mobilepush/modals.js +19 -0
  27. package/v2Components/FormBuilder/Functional/channels/mobilepush/runSubmitPipeline.js +45 -0
  28. package/v2Components/FormBuilder/Functional/channels/mobilepush/tests/getEditorErrorDescriptor.test.js +162 -0
  29. package/v2Components/FormBuilder/Functional/channels/mobilepush/tests/modals.test.js +37 -0
  30. package/v2Components/FormBuilder/Functional/channels/mobilepush/tests/runSubmitPipeline.test.js +70 -0
  31. package/v2Components/FormBuilder/Functional/channels/mobilepush/tests/validate.test.js +274 -0
  32. package/v2Components/FormBuilder/Functional/channels/mobilepush/validate.js +251 -0
  33. package/v2Components/FormBuilder/Functional/channels/registry.js +2 -0
  34. package/v2Components/FormBuilder/Functional/constants.js +43 -8
  35. package/v2Components/FormBuilder/Functional/core/schema/initializeFormState.js +175 -28
  36. package/v2Components/FormBuilder/Functional/core/store/formReducer.js +75 -6
  37. package/v2Components/FormBuilder/Functional/core/store/toLegacyFormData.js +10 -20
  38. package/v2Components/FormBuilder/Functional/layout/FieldSlot.js +9 -1
  39. package/v2Components/FormBuilder/Functional/layout/SchemaForm.js +20 -3
  40. package/v2Components/FormBuilder/Functional/layout/Section.js +6 -1
  41. package/v2Components/FormBuilder/Functional/layout/TabsContainer.js +89 -0
  42. package/v2Components/FormBuilder/Functional/renderers/mpushRenderers.js +440 -0
  43. package/v2Components/FormBuilder/Functional/tests/fieldSlot.test.js +9 -2
  44. package/v2Components/FormBuilder/Functional/tests/mpush.crossFlowParity.test.js +430 -0
  45. package/v2Components/FormBuilder/Functional/tests/mpush.ctaFlows.parity.test.js +282 -0
  46. package/v2Components/FormBuilder/Functional/tests/mpush.editContainer.parity.test.js +141 -0
  47. package/v2Components/FormBuilder/Functional/tests/mpush.engine.test.js +306 -0
  48. package/v2Components/FormBuilder/Functional/tests/mpush.shellEvents.test.js +151 -0
  49. package/v2Components/FormBuilder/Functional/tests/mpushRenderers.test.js +342 -0
  50. package/v2Components/FormBuilder/Functional/tests/schemaForm.test.js +17 -0
  51. package/v2Components/FormBuilder/Functional/tests/tabsContainer.test.js +110 -0
  52. package/v2Components/FormBuilder/index.js +34 -12
  53. package/v2Components/FormBuilder/tests/entryGate.test.js +94 -7
  54. package/v2Components/FormBuilder/tests/mpush.characterization.test.js +459 -0
  55. package/v2Components/TestAndPreviewSlidebox/index.js +0 -9
  56. package/v2Containers/CommunicationFlow/CommunicationFlow.js +75 -140
  57. package/v2Containers/CommunicationFlow/CommunicationFlow.scss +2 -18
  58. package/v2Containers/CommunicationFlow/CommunicationFlowCard.js +27 -58
  59. package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +51 -503
  60. package/v2Containers/CommunicationFlow/Tests/CommunicationFlowCard.test.js +4 -40
  61. package/v2Containers/CommunicationFlow/constants.js +4 -65
  62. package/v2Containers/CommunicationFlow/index.js +23 -16
  63. package/v2Containers/CommunicationFlow/messages.js +3 -11
  64. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.js +11 -24
  65. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.scss +0 -4
  66. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +4 -176
  67. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.js +0 -3
  68. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.js +0 -6
  69. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +0 -25
  70. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/deliverySettingsConfig.test.js +4 -11
  71. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/deliverySettingsConfig.js +5 -19
  72. package/v2Containers/CreativesContainer/index.js +3 -2
  73. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +5 -5
  74. package/v2Containers/CreativesContainer/tests/index.test.js +0 -12
  75. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +19 -115
  76. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +3 -27
  77. package/v2Containers/WebPush/Create/preview/WebPushPreview.js +12 -17
  78. package/v2Containers/WebPush/Create/preview/tests/WebPushPreview.test.js +0 -5
  79. package/v2Containers/Whatsapp/index.js +3 -6
  80. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -306
  81. package/v2Containers/mockdata.js +0 -25
  82. package/v2Containers/CommunicationFlow/Tests/constants.test.js +0 -85
@@ -108,22 +108,6 @@ describe('CommunicationFlowCard', () => {
108
108
  fireEvent.click(screen.getByText('Add creatives'));
109
109
  expect(screen.getByTestId('comm-flow-mock')).toHaveAttribute('data-mode', 'create');
110
110
  });
111
-
112
- it('disables the Add creatives button when disabled prop is true', () => {
113
- renderCard({ disabled: true, disabledTooltip: 'Enter an Alert name to add content' });
114
- expect(screen.getByText('Add creatives').closest('button')).toBeDisabled();
115
- });
116
-
117
- it('does not open SlideBox when Add creatives is clicked while disabled', () => {
118
- renderCard({ disabled: true, disabledTooltip: 'Enter an Alert name to add content' });
119
- fireEvent.click(screen.getByText('Add creatives'));
120
- expect(screen.queryByTestId('slide-box')).not.toBeInTheDocument();
121
- });
122
-
123
- it('Add creatives button is enabled by default (disabled prop absent)', () => {
124
- renderCard();
125
- expect(screen.getByText('Add creatives').closest('button')).not.toBeDisabled();
126
- });
127
111
  });
128
112
 
129
113
  describe('configured state (initialData provided)', () => {
@@ -202,26 +186,6 @@ describe('CommunicationFlowCard', () => {
202
186
  renderCard({ initialData: makeSavedData() });
203
187
  expect(screen.getByText('Other controls')).toBeInTheDocument();
204
188
  });
205
-
206
- it('hides the Other controls column when config.features.showDynamicControls is false', () => {
207
- renderCard({
208
- config: { ...baseConfig, features: { showDynamicControls: false } },
209
- initialData: makeSavedData({
210
- dynamicControls: {
211
- sendToControlCustomers: false, sendToBrandPocs: false, useTinyUrl: false, overrideDailyLimit: false,
212
- },
213
- }),
214
- });
215
- expect(screen.queryByText('Other controls')).not.toBeInTheDocument();
216
- });
217
-
218
- it('still shows the Other controls column when showDynamicControls is unset (Engagement Module default)', () => {
219
- renderCard({
220
- config: { ...baseConfig, features: {} },
221
- initialData: makeSavedData(),
222
- });
223
- expect(screen.getByText('Other controls')).toBeInTheDocument();
224
- });
225
189
  });
226
190
 
227
191
  describe('channel config resolution', () => {
@@ -267,11 +231,11 @@ describe('CommunicationFlowCard', () => {
267
231
  expect(screen.getByText(/Sender ID/)).toBeInTheDocument();
268
232
  });
269
233
 
270
- it('shows senderId for EMAIL with Sender ID label', () => {
234
+ it('shows senderEmail for EMAIL with Sender ID label', () => {
271
235
  renderCard({
272
236
  initialData: makeSavedData({
273
237
  contentItems: [{ channel: 'EMAIL', templateData: {} }],
274
- deliverySetting: { channelSetting: { EMAIL: { senderId: 'test@example.com' } } },
238
+ deliverySetting: { channelSetting: { EMAIL: { senderEmail: 'test@example.com' } } },
275
239
  }),
276
240
  });
277
241
  expect(screen.getByText(/test@example.com/)).toBeInTheDocument();
@@ -581,10 +545,10 @@ describe('CommunicationFlowCard', () => {
581
545
  });
582
546
 
583
547
  describe('SlideBox header', () => {
584
- it('renders Add content title in the slidebox header', () => {
548
+ it('renders Add message title in the slidebox header', () => {
585
549
  renderCard();
586
550
  fireEvent.click(screen.getByText('Add creatives'));
587
- expect(screen.getByText('Add content')).toBeInTheDocument();
551
+ expect(screen.getByText('Add message')).toBeInTheDocument();
588
552
  });
589
553
  });
590
554
 
@@ -38,68 +38,9 @@ export const CHANNEL_PRIORITY = 'CHANNEL_PRIORITY';
38
38
  export const AB_TEST = 'AB_TEST';
39
39
  export const SINGLE_TEMPLATE = 'SINGLE_TEMPLATE';
40
40
 
41
- // CCS create is only supported for SINGLE (CHANNEL_PRIORITY/AB_TEST skip CCS entirely).
42
- export const CCS_STRATEGY_TYPE_SINGLE = 'SINGLE';
43
-
44
- // Mirrors CCS SingleChannelStrategy field names; kept separate from the legacy
45
- // messageMeta CHANNEL_CONTENT_KEY_MAP/CHANNEL_DELIVERY_KEY_MAP.
46
- export const CCS_CHANNEL_CONTENT_KEY_MAP = {
47
- SMS: 'smsMessageContent',
48
- EMAIL: 'emailMessageContent',
49
- WHATSAPP: 'whatsappMessageContent',
50
- // CCS's channel enum for mobile push is 'PUSH', but content/delivery keys stay 'mpush*'.
51
- PUSH: 'mpushMessageContent',
52
- INAPP: 'inAppMessageContent',
53
- ANDROID: 'androidMessageContent',
54
- IOS: 'iosMessageContent',
55
- ZALO: 'zaloMessageContent',
56
- VIBER: 'viberMessageContent',
57
- LINE: 'lineMessageContent',
58
- WEBPUSH: 'webPushMessageContent',
59
- RCS: 'rcsMessageContent',
60
- };
61
-
62
- // Transforms legacy messageMeta field names to CCS's before sending, for channels
63
- // where the two shapes don't already match.
64
- export const CCS_CONTENT_TRANSFORMS = {
65
- EMAIL: (templateData = {}) => ({
66
- messageSubject: templateData.emailSubject,
67
- messageBody: templateData.emailBody || templateData.emailHtml,
68
- }),
69
- SMS: (templateData = {}) => ({
70
- message: templateData.messageBody || '',
71
- }),
72
- WEBPUSH: (templateData = {}) => {
73
- const inner = templateData.messageContent?.content || templateData;
74
- return {
75
- messageSubject: inner.messageSubject,
76
- accountId: inner.accountId,
77
- content: inner.content,
78
- };
79
- },
80
- };
81
-
82
- export const CCS_CHANNEL_DELIVERY_KEY_MAP = {
83
- SMS: 'smsDeliverySettings',
84
- EMAIL: 'emailDeliverySettings',
85
- WHATSAPP: 'whatsappDeliverySettings',
86
- PUSH: 'mpushDeliverySettings',
87
- INAPP: 'inAppDeliverySettings',
88
- ANDROID: 'androidDeliverySettings',
89
- IOS: 'iosDeliverySettings',
90
- ZALO: 'zaloDeliverySettings',
91
- VIBER: 'viberDeliverySettings',
92
- LINE: 'lineDeliverySettings',
93
- WEBPUSH: 'webPushDeliverySettings',
94
- RCS: 'rcsDeliverySettings',
95
- };
96
-
97
- // UI uses `MOBILEPUSH`; CCS uses `PUSH` for the channel enum. Other channels pass through.
98
- export const CCS_CHANNEL_NAME_MAP = {
99
- MOBILEPUSH: 'PUSH',
100
- };
101
-
102
- // Channel config is the shared source of truth: paneKey maps TemplatesV2 panes, channelProp maps CreativesContainer tabs.
41
+ // Channel config - single source of truth for CreativesContainer and TemplatesV2
42
+ // paneKey: TemplatesV2 defaultPanes object key (for channelsToHide)
43
+ // channelProp: CreativesContainer channel prop (must match pane.key for tab to be active)
103
44
  export const CHANNELS = [
104
45
  {
105
46
  value: 'SMS',
@@ -291,6 +232,4 @@ export const INCENTIVE_TYPES = [
291
232
  {
292
233
  value: 'badges', label: <FormattedMessage {...messages.badges} />, isActive: false, disabled: false,
293
234
  },
294
- ];
295
-
296
- export const REFERENCE_ID_EXISTS = 'REFERENCE_ID_EXISTS';
235
+ ];
@@ -9,6 +9,7 @@
9
9
  import React from 'react';
10
10
  import PropTypes from 'prop-types';
11
11
  import CommunicationFlow from './CommunicationFlow';
12
+ import { hasSupportEngagementModule } from '../../utils/common';
12
13
 
13
14
  const CommunicationFlowContainer = ({
14
15
  config,
@@ -17,20 +18,26 @@ const CommunicationFlowContainer = ({
17
18
  onCancel,
18
19
  onChange,
19
20
  ...otherProps
20
- }) => (
21
- <CommunicationFlow
22
- config={config}
23
- initialData={initialData}
24
- onSave={onSave}
25
- onCancel={onCancel}
26
- onChange={onChange}
27
- {...otherProps}
28
- />
29
- );
21
+ }) => {
22
+ if (!hasSupportEngagementModule()) {
23
+ return null;
24
+ }
25
+
26
+ return (
27
+ <CommunicationFlow
28
+ config={config}
29
+ initialData={initialData}
30
+ onSave={onSave}
31
+ onCancel={onCancel}
32
+ onChange={onChange}
33
+ {...otherProps}
34
+ />
35
+ );
36
+ };
30
37
 
31
38
  CommunicationFlowContainer.propTypes = {
32
39
  config: PropTypes.shape({
33
- consumer: PropTypes.oneOf(['campaigns', 'loyalty', 'adiona', 'coupons', 'cartPromotions']).isRequired,
40
+ consumer: PropTypes.oneOf(['campaigns', 'loyalty', 'adiona']).isRequired,
34
41
  channel: PropTypes.string, // optional if channel step is skipped
35
42
  mode: PropTypes.oneOf(['create', 'edit', 'preview']).isRequired,
36
43
  channelsToHide: PropTypes.arrayOf(PropTypes.string),
@@ -80,13 +87,13 @@ CommunicationFlowContainer.propTypes = {
80
87
  controls: PropTypes.array,
81
88
  }),
82
89
  }),
83
- context: PropTypes.object, // ouId, campaignId, programId, name, referenceId, description
84
- useCCS: PropTypes.bool, // skips the CCS createCommDefinition call on save when false; defaults to true
90
+ context: PropTypes.object, // ouId, campaignId, programId, etc.
91
+ useCCS: PropTypes.bool, // If false, skips CCS bulk-claim-approve on save. Defaults to true.
85
92
  }).isRequired,
86
93
  initialData: PropTypes.object, // for edit/preview mode
87
- onSave: PropTypes.func.isRequired, // (data) => void, data.ccsCommDefinition set when CCS create succeeded
88
- onCancel: PropTypes.func.isRequired, // () => void
89
- onChange: PropTypes.func, // (data) => void
94
+ onSave: PropTypes.func.isRequired, // (data) => void - called when user saves
95
+ onCancel: PropTypes.func.isRequired, // () => void - called when user cancels
96
+ onChange: PropTypes.func, // (data) => void - optional, called on data changes
90
97
  };
91
98
 
92
99
  CommunicationFlowContainer.defaultProps = {
@@ -64,9 +64,9 @@ export default {
64
64
  },
65
65
 
66
66
  // Slidebox header
67
- addContent: {
68
- id: `${prefix}.addContent`,
69
- defaultMessage: 'Add content',
67
+ addMessage: {
68
+ id: `${prefix}.addMessage`,
69
+ defaultMessage: 'Add message',
70
70
  },
71
71
 
72
72
  // Step prompts
@@ -363,12 +363,4 @@ export default {
363
363
  id: `${prefix}.senderNotConfiguredError`,
364
364
  defaultMessage: 'Selected domain gateway id is not correct. Please change the domain id or contact the gateway team to register them with Capillary.',
365
365
  },
366
- duplicateReferenceIdError: {
367
- id: `${prefix}.duplicateReferenceIdError`,
368
- defaultMessage: 'This reference ID is already in use in your organization. Please use a different reference ID.',
369
- },
370
- genericSaveError: {
371
- id: `${prefix}.genericSaveError`,
372
- defaultMessage: 'Something went wrong while saving your content. Please try again.',
373
- },
374
366
  };
@@ -45,7 +45,6 @@ const ChannelSelectionStep = ({
45
45
  onChange,
46
46
  channelsToHide = [],
47
47
  channelsToDisable = [],
48
- disablePreviewAndTest = false,
49
48
  creativesMode = 'create',
50
49
  selectedOfferDetails = [],
51
50
  incentivesData,
@@ -64,7 +63,6 @@ const ChannelSelectionStep = ({
64
63
  const [showIncentivesMenuMap, setShowIncentivesMenuMap] = useState({});
65
64
  const [showTestAndPreview, setShowTestAndPreview] = useState(false);
66
65
  const [testAndPreviewItem, setTestAndPreviewItem] = useState(null);
67
- const [domainPropertiesData, setDomainPropertiesData] = useState(null);
68
66
  const { formatMessage } = intl || {};
69
67
 
70
68
  // Available channels (filter out hidden ones)
@@ -78,19 +76,6 @@ const ChannelSelectionStep = ({
78
76
  const selectedChannelLowerCase = selectedChannel?.toLowerCase();
79
77
  return CHANNELS.find((channel) => channel?.value?.toLowerCase() === selectedChannelLowerCase || channel?.channelProp === selectedChannelLowerCase) || null;
80
78
  }, [selectedChannel]);
81
-
82
- const editingZaloHostName = useMemo(() => {
83
- if (!editingContentId) return '';
84
- const editingItem = contentItems.find((c) => c.contentId === editingContentId);
85
- if (editingItem?.channel?.toUpperCase() !== ZALO) return '';
86
- const accountId = editingItem?.templateData?.accountId;
87
- if (!accountId) return '';
88
- const zaloDomains = domainPropertiesData?.ZALO || [];
89
- const matchedDomain = zaloDomains.find(
90
- (domain) => String(domain?.domainProperties?.connectionProperties?.oa_id) === String(accountId),
91
- );
92
- return matchedDomain?.domainProperties?.hostName || '';
93
- }, [editingContentId, contentItems, domainPropertiesData]);
94
79
  /**
95
80
  * Handle CreativesContainer close
96
81
  */
@@ -373,7 +358,7 @@ const ChannelSelectionStep = ({
373
358
  >
374
359
  {formatMessage(messages.edit)}
375
360
  </CapMenu.Item>
376
- {!disablePreviewAndTest && !PREVIEW_TEST_UNSUPPORTED_CHANNELS.includes(item?.channel?.toUpperCase()) && (
361
+ {!PREVIEW_TEST_UNSUPPORTED_CHANNELS.includes(item?.channel?.toUpperCase()) && (
377
362
  <CapMenu.Item
378
363
  id="preview-menu-item"
379
364
  className="ant-dropdown-menu-item"
@@ -422,6 +407,10 @@ const ChannelSelectionStep = ({
422
407
  return (
423
408
  <>
424
409
  <CapRow className="channel-selection-step">
410
+ <CapHeading type="h3" className="margin-b-8">
411
+ {formatMessage(messages.contentTemplate)}
412
+ </CapHeading>
413
+
425
414
  <CapRow
426
415
  useLegacy
427
416
  className={`content-template-container ${contentItems?.length > 0 ? 'content-template-container--has-content' : ''}`}
@@ -433,7 +422,7 @@ const ChannelSelectionStep = ({
433
422
  )}
434
423
 
435
424
  {contentItems?.length === 0 ? (
436
- <CapRow className={`content-template-section ${contentItems?.length === 0 ? 'no-content-items' : ''}`}>
425
+ <CapRow className="content-template-section">
437
426
  <CapDropdown
438
427
  overlay={renderChannelDropdownOverlay() || <CapMenu />}
439
428
  onVisibleChange={handleChannelsVisibleChange}
@@ -481,7 +470,6 @@ const ChannelSelectionStep = ({
481
470
  deliverySettingsData={deliverySettingsData}
482
471
  deliverySetting={value?.deliverySetting}
483
472
  onDeliverySettingChange={(deliverySetting) => onChange({ deliverySetting })}
484
- onDomainPropertiesLoaded={setDomainPropertiesData}
485
473
  intl={intl}
486
474
  />
487
475
  )}
@@ -502,13 +490,14 @@ const ChannelSelectionStep = ({
502
490
  getCreativesData={handleCreativesData}
503
491
  handleCloseCreatives={handleCloseCreatives}
504
492
  isFullMode={false}
505
- hostName={editingZaloHostName}
506
- messageDetails={{ type: (config?.context?.module || '').toLowerCase() || 'default' }}
507
- location={{ query: { type: 'embedded', module: config?.context?.module } }}
493
+ messageDetails={{ type: 'default' }}
508
494
  templateData={editingContentId ? (() => {
509
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.
510
499
  if (saved?.channel?.toUpperCase() === WEBPUSH && saved?.messageContent?.content) {
511
- return { ...saved.messageContent.content, type: WEBPUSH, channel: WEBPUSH };
500
+ return { ...saved.messageContent.content, type: WEBPUSH };
512
501
  }
513
502
  return saved;
514
503
  })() : null}
@@ -566,7 +555,6 @@ ChannelSelectionStep.propTypes = {
566
555
  onChange: PropTypes.func.isRequired,
567
556
  channelsToHide: PropTypes.array,
568
557
  channelsToDisable: PropTypes.array,
569
- disablePreviewAndTest: PropTypes.bool,
570
558
  creativesMode: PropTypes.oneOf(['create', 'edit', 'preview']),
571
559
  selectedOfferDetails: PropTypes.array,
572
560
  incentivesData: PropTypes.shape({
@@ -588,7 +576,6 @@ ChannelSelectionStep.defaultProps = {
588
576
  error: null,
589
577
  channelsToHide: [],
590
578
  channelsToDisable: [],
591
- disablePreviewAndTest: false,
592
579
  creativesMode: 'create',
593
580
  selectedOfferDetails: [],
594
581
  incentivesData: null,
@@ -130,10 +130,6 @@
130
130
  border-bottom: 1px solid $CAP_G07;
131
131
  }
132
132
 
133
- .no-content-items {
134
- border-bottom: none;
135
- }
136
-
137
133
  .add-content-template-button {
138
134
  padding: $CAP_SPACE_12 $CAP_SPACE_16;
139
135
  align-self: center !important;
@@ -37,21 +37,9 @@ jest.mock('../../../../CreativesContainer', () => function MockCreativesContaine
37
37
  handleCloseCreatives,
38
38
  creativesMode,
39
39
  channel,
40
- templateData,
41
- hostName,
42
- messageDetails,
43
- location,
44
40
  }) {
45
41
  return (
46
- <div
47
- data-testid="creatives-mock"
48
- data-creatives-mode={creativesMode}
49
- data-creatives-channel={channel}
50
- data-template-data={JSON.stringify(templateData)}
51
- data-host-name={hostName}
52
- data-message-details={JSON.stringify(messageDetails)}
53
- data-location={JSON.stringify(location)}
54
- >
42
+ <div data-testid="creatives-mock" data-creatives-mode={creativesMode} data-creatives-channel={channel}>
55
43
  <button
56
44
  type="button"
57
45
  data-testid="creatives-save"
@@ -74,7 +62,7 @@ jest.mock('../../../../CreativesContainer', () => function MockCreativesContaine
74
62
  });
75
63
 
76
64
  jest.mock('../../DeliverySettingsStep', () => ({
77
- DeliverySettingsSection: function MockDeliverySettings({ onDeliverySettingChange, onDomainPropertiesLoaded }) {
65
+ DeliverySettingsSection: function MockDeliverySettings({ onDeliverySettingChange }) {
78
66
  return (
79
67
  <div data-testid="delivery-settings-section">
80
68
  <button
@@ -84,23 +72,6 @@ jest.mock('../../DeliverySettingsStep', () => ({
84
72
  >
85
73
  Apply delivery
86
74
  </button>
87
- <button
88
- type="button"
89
- data-testid="domain-properties-loaded"
90
- onClick={() => onDomainPropertiesLoaded?.({
91
- ZALO: [{
92
- id: 267284,
93
- domainProperties: {
94
- id: 4977,
95
- domainName: 'Gapit_Automation',
96
- connectionProperties: { oa_id: '300086756699856746' },
97
- hostName: 'gapitzalotrans',
98
- },
99
- }],
100
- })}
101
- >
102
- Load domain properties
103
- </button>
104
75
  </div>
105
76
  );
106
77
  },
@@ -123,7 +94,7 @@ describe('ChannelSelectionStep', () => {
123
94
  console.log.mockRestore();
124
95
  });
125
96
 
126
- it('empty state: shows the Channel heading and add-template entry point', () => {
97
+ it('empty state: shows content template heading and add-template entry point', () => {
127
98
  const onChange = jest.fn();
128
99
  renderStep(
129
100
  <ChannelSelectionStep
@@ -133,7 +104,7 @@ describe('ChannelSelectionStep', () => {
133
104
  />,
134
105
  );
135
106
 
136
- expect(screen.getAllByText('Channel').length).toBeGreaterThanOrEqual(1);
107
+ expect(screen.getAllByText('Content template').length).toBeGreaterThanOrEqual(1);
137
108
  expect(screen.getByRole('button', { name: /add creative/i })).toBeInTheDocument();
138
109
  expect(screen.queryByTestId('creatives-mock')).not.toBeInTheDocument();
139
110
  });
@@ -1712,149 +1683,6 @@ describe('ChannelSelectionStep', () => {
1712
1683
  expect(screen.getByTestId('creatives-mock')).toHaveAttribute('data-creatives-mode', 'edit');
1713
1684
  });
1714
1685
 
1715
- it('edit WEBPUSH item includes `channel: WEBPUSH` (not just `type`) in the unwrapped templateData (regression: CreativesContainer.getTemplateData\'s switch reads templateData.channel, not templateData.type — without it the switch never matched WEBPUSH and the edit screen rendered blank below its header)', async () => {
1716
- renderStep(
1717
- <ChannelSelectionStep
1718
- value={{
1719
- contentItems: [{
1720
- contentId: 'wp-edit-channel',
1721
- channel: 'WEBPUSH',
1722
- templateData: {
1723
- channel: 'WEBPUSH',
1724
- messageContent: {
1725
- content: { messageSubject: 'dasd', accountId: 13792, content: { title: 'dasd', message: 'dasd' } },
1726
- },
1727
- },
1728
- }],
1729
- }}
1730
- onChange={jest.fn()}
1731
- channels={CHANNELS}
1732
- />,
1733
- );
1734
- await userEvent.click(screen.getByLabelText('Show more content options icon'));
1735
- await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
1736
- await userEvent.click(within(screen.getByRole('menu')).getByText('Edit'));
1737
- await waitFor(() => expect(screen.getByTestId('creatives-mock')).toBeInTheDocument(), WAIT_OPTIONS);
1738
- const passedTemplateData = JSON.parse(screen.getByTestId('creatives-mock').getAttribute('data-template-data'));
1739
- expect(passedTemplateData.channel).toBe('WEBPUSH');
1740
- expect(passedTemplateData.type).toBe('WEBPUSH');
1741
- expect(passedTemplateData.messageSubject).toBe('dasd');
1742
- expect(passedTemplateData.accountId).toBe(13792);
1743
- });
1744
-
1745
- // ── ZALO edit: hostName resolved from domainProperties ────────────────────────
1746
-
1747
- it('resolves a Zalo item\'s hostName from the domainProperties fetch by matching accountId to connectionProperties.oa_id, and passes it to CreativesContainer (regression: CCS\'s zaloMessageContent never carries hostName, and without it Zalo/index.js\'s getTemplateInfoById guard never passes, so the edit view never loads live template data)', async () => {
1748
- renderStep(
1749
- <ChannelSelectionStep
1750
- value={{
1751
- contentItems: [{
1752
- contentId: 'zalo-edit',
1753
- channel: 'ZALO',
1754
- templateData: {
1755
- channel: 'ZALO',
1756
- accountId: '300086756699856746',
1757
- accountName: 'gapit_automation_account',
1758
- token: 'zalo-token',
1759
- templateConfigs: { id: '630142', name: '1592_Chí Linh_D1' },
1760
- },
1761
- }],
1762
- }}
1763
- onChange={jest.fn()}
1764
- channels={CHANNELS}
1765
- deliverySettingsData={{ required: false }}
1766
- />,
1767
- );
1768
- await userEvent.click(screen.getByTestId('domain-properties-loaded'));
1769
- await userEvent.click(screen.getByLabelText('Show more content options icon'));
1770
- await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
1771
- await userEvent.click(within(screen.getByRole('menu')).getByText('Edit'));
1772
- await waitFor(() => expect(screen.getByTestId('creatives-mock')).toBeInTheDocument(), WAIT_OPTIONS);
1773
- expect(screen.getByTestId('creatives-mock')).toHaveAttribute('data-host-name', 'gapitzalotrans');
1774
- });
1775
-
1776
- it('resolves an empty hostName for a Zalo item when no matching domainProperties account is loaded', async () => {
1777
- renderStep(
1778
- <ChannelSelectionStep
1779
- value={{
1780
- contentItems: [{
1781
- contentId: 'zalo-edit-no-match',
1782
- channel: 'ZALO',
1783
- templateData: { channel: 'ZALO', accountId: 'unmatched-account-id' },
1784
- }],
1785
- }}
1786
- onChange={jest.fn()}
1787
- channels={CHANNELS}
1788
- deliverySettingsData={{ required: false }}
1789
- />,
1790
- );
1791
- await userEvent.click(screen.getByLabelText('Show more content options icon'));
1792
- await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
1793
- await userEvent.click(within(screen.getByRole('menu')).getByText('Edit'));
1794
- await waitFor(() => expect(screen.getByTestId('creatives-mock')).toBeInTheDocument(), WAIT_OPTIONS);
1795
- expect(screen.getByTestId('creatives-mock')).toHaveAttribute('data-host-name', '');
1796
- });
1797
-
1798
- // ── Tag-context pass-through (config.context.module) ──────────────────────────
1799
-
1800
- it('passes config.context.module through to messageDetails.type (lowercased) and location.query.module, unchanged, for tag-fetch scoping (regression: this used to be hardcoded to messageDetails={{type: \'default\'}} with no location prop at all, so every consumer\'s tag-fetch was scoped generically instead of per-consumer)', async () => {
1801
- renderStep(
1802
- <ChannelSelectionStep
1803
- value={{ contentItems: [] }}
1804
- onChange={jest.fn()}
1805
- channels={CHANNELS}
1806
- config={{ context: { module: 'CAMPAIGNS' } }}
1807
- />,
1808
- );
1809
- await userEvent.click(screen.getByRole('button', { name: /add creative/i }));
1810
- await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
1811
- await userEvent.click(within(screen.getByRole('menu')).getByText('SMS'));
1812
- await waitFor(() => expect(screen.getByTestId('creatives-mock')).toBeInTheDocument(), WAIT_OPTIONS);
1813
-
1814
- const mock = screen.getByTestId('creatives-mock');
1815
- expect(JSON.parse(mock.getAttribute('data-message-details'))).toEqual({ type: 'campaigns' });
1816
- expect(JSON.parse(mock.getAttribute('data-location'))).toEqual({
1817
- query: { type: 'embedded', module: 'CAMPAIGNS' },
1818
- });
1819
- });
1820
-
1821
- it('passes through whatever module value a coupons/cartPromotions-style config sets, with no special-casing on our end', async () => {
1822
- renderStep(
1823
- <ChannelSelectionStep
1824
- value={{ contentItems: [] }}
1825
- onChange={jest.fn()}
1826
- channels={CHANNELS}
1827
- config={{ context: { module: 'coupons' } }}
1828
- />,
1829
- );
1830
- await userEvent.click(screen.getByRole('button', { name: /add creative/i }));
1831
- await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
1832
- await userEvent.click(within(screen.getByRole('menu')).getByText('SMS'));
1833
- await waitFor(() => expect(screen.getByTestId('creatives-mock')).toBeInTheDocument(), WAIT_OPTIONS);
1834
-
1835
- const mock = screen.getByTestId('creatives-mock');
1836
- expect(JSON.parse(mock.getAttribute('data-message-details'))).toEqual({ type: 'coupons' });
1837
- expect(JSON.parse(mock.getAttribute('data-location'))).toEqual({
1838
- query: { type: 'embedded', module: 'coupons' },
1839
- });
1840
- });
1841
-
1842
- it('falls back to messageDetails={{type: "default"}} when config.context.module is absent (unset consumers keep prior behavior)', async () => {
1843
- renderStep(
1844
- <ChannelSelectionStep
1845
- value={{ contentItems: [] }}
1846
- onChange={jest.fn()}
1847
- channels={CHANNELS}
1848
- />,
1849
- );
1850
- await userEvent.click(screen.getByRole('button', { name: /add creative/i }));
1851
- await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
1852
- await userEvent.click(within(screen.getByRole('menu')).getByText('SMS'));
1853
- await waitFor(() => expect(screen.getByTestId('creatives-mock')).toBeInTheDocument(), WAIT_OPTIONS);
1854
-
1855
- expect(JSON.parse(screen.getByTestId('creatives-mock').getAttribute('data-message-details'))).toEqual({ type: 'default' });
1856
- });
1857
-
1858
1686
  // ── FTP channel filtered from dropdown ───────────────────────────────────────
1859
1687
 
1860
1688
  it('FTP channel is filtered out of the channel dropdown', async () => {
@@ -33,7 +33,6 @@ const DeliverySettingsSection = ({
33
33
  deliverySettingsData,
34
34
  deliverySetting = {},
35
35
  onDeliverySettingChange,
36
- onDomainPropertiesLoaded,
37
36
  intl,
38
37
  }) => {
39
38
  const [showSlidebox, setShowSlidebox] = useState(false);
@@ -103,7 +102,6 @@ const DeliverySettingsSection = ({
103
102
  )
104
103
  : raw;
105
104
  setDomainPropertiesData(entity);
106
- onDomainPropertiesLoaded?.(entity);
107
105
  }
108
106
  } catch (err) {
109
107
  if (!cancelled) setDomainPropertiesData(null);
@@ -318,7 +316,6 @@ DeliverySettingsSection.propTypes = {
318
316
  deliverySettingsData: PropTypes.object,
319
317
  deliverySetting: PropTypes.object,
320
318
  onDeliverySettingChange: PropTypes.func,
321
- onDomainPropertiesLoaded: PropTypes.func,
322
319
  intl: PropTypes.object.isRequired,
323
320
  };
324
321
 
@@ -29,7 +29,6 @@ import {
29
29
  getEmailDefaultsForDomainId,
30
30
  getSmsDefaultsForDomainId,
31
31
  getRcsAccountName,
32
- getEmailDomainGatewayMapIdByDomainId,
33
32
  } from './deliverySettingsConfig';
34
33
  import messages from '../../messages';
35
34
  import './SenderDetails.scss';
@@ -152,11 +151,6 @@ const SenderDetails = ({
152
151
  saveValues.whatsappDomainId = matchedDomain?.domainProperties?.id ?? matchedDomain?.domainId ?? matchedDomain?.id;
153
152
  }
154
153
  }
155
- // Include EMAIL domainGatewayMapId derived from the selected domain for persistence -
156
- // CCS needs it alongside domainId, but it's not a user-facing field.
157
- if (entity && fieldValues.emailDomain != null && fieldValues.emailDomain !== '') {
158
- saveValues.emailDomainGatewayMapId = getEmailDomainGatewayMapIdByDomainId(entity, fieldValues.emailDomain);
159
- }
160
154
  onSave(saveValues);
161
155
  }
162
156
  onClose();