@capillarytech/creatives-library 8.0.345-alpha.12 → 8.0.345-alpha.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 (129) hide show
  1. package/constants/unified.js +0 -29
  2. package/package.json +1 -1
  3. package/services/tests/api.test.js +0 -13
  4. package/utils/commonUtils.js +1 -19
  5. package/v2Components/CapActionButton/constants.js +0 -7
  6. package/v2Components/CapActionButton/index.js +109 -167
  7. package/v2Components/CapActionButton/index.scss +6 -157
  8. package/v2Components/CapActionButton/messages.js +3 -19
  9. package/v2Components/CapActionButton/tests/index.test.js +17 -41
  10. package/v2Components/CapTagList/index.js +0 -10
  11. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +49 -70
  12. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
  13. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -207
  14. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
  15. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +10 -85
  16. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
  17. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -79
  18. package/v2Components/CommonTestAndPreview/SendTestMessage.js +5 -10
  19. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +15 -160
  20. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +76 -341
  21. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +4 -133
  22. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +0 -11
  23. package/v2Components/CommonTestAndPreview/constants.js +2 -38
  24. package/v2Components/CommonTestAndPreview/index.js +186 -676
  25. package/v2Components/CommonTestAndPreview/messages.js +3 -49
  26. package/v2Components/CommonTestAndPreview/sagas.js +6 -15
  27. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +284 -308
  28. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +65 -231
  29. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +5 -118
  30. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -341
  31. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +1 -8
  32. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +13 -34
  33. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +283 -281
  34. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
  35. package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -132
  36. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
  37. package/v2Components/FormBuilder/index.js +10 -8
  38. package/v2Components/TemplatePreview/_templatePreview.scss +23 -33
  39. package/v2Components/TemplatePreview/index.js +28 -143
  40. package/v2Components/TemplatePreview/tests/index.test.js +0 -142
  41. package/v2Components/TestAndPreviewSlidebox/index.js +1 -13
  42. package/v2Components/TestAndPreviewSlidebox/sagas.js +4 -11
  43. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +1 -3
  44. package/v2Containers/CreativesContainer/SlideBoxContent.js +4 -36
  45. package/v2Containers/CreativesContainer/SlideBoxFooter.js +1 -10
  46. package/v2Containers/CreativesContainer/SlideBoxHeader.js +4 -29
  47. package/v2Containers/CreativesContainer/constants.js +0 -9
  48. package/v2Containers/CreativesContainer/index.js +103 -300
  49. package/v2Containers/CreativesContainer/index.scss +1 -51
  50. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -78
  51. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
  52. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
  53. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +98 -357
  54. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +15 -20
  55. package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
  56. package/v2Containers/Email/reducer.js +11 -3
  57. package/v2Containers/Email/sagas.js +9 -5
  58. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +4 -0
  59. package/v2Containers/Email/tests/sagas.test.js +21 -3
  60. package/v2Containers/Rcs/constants.js +8 -119
  61. package/v2Containers/Rcs/index.js +812 -2375
  62. package/v2Containers/Rcs/index.scss +6 -276
  63. package/v2Containers/Rcs/messages.js +3 -38
  64. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +70345 -98302
  65. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +5 -0
  66. package/v2Containers/Rcs/tests/index.test.js +121 -152
  67. package/v2Containers/Rcs/tests/mockData.js +0 -38
  68. package/v2Containers/Rcs/tests/utils.test.js +30 -646
  69. package/v2Containers/Rcs/utils.js +11 -478
  70. package/v2Containers/Sms/Create/index.js +40 -100
  71. package/v2Containers/SmsTrai/Create/index.js +4 -9
  72. package/v2Containers/SmsTrai/Edit/constants.js +0 -2
  73. package/v2Containers/SmsTrai/Edit/index.js +130 -636
  74. package/v2Containers/SmsTrai/Edit/messages.js +4 -14
  75. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2296 -4249
  76. package/v2Containers/SmsWrapper/index.js +8 -37
  77. package/v2Containers/TagList/index.js +0 -6
  78. package/v2Containers/Templates/_templates.scss +2 -163
  79. package/v2Containers/Templates/actions.js +0 -11
  80. package/v2Containers/Templates/constants.js +0 -2
  81. package/v2Containers/Templates/index.js +54 -119
  82. package/v2Containers/Templates/sagas.js +12 -57
  83. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1079 -1043
  84. package/v2Containers/Templates/tests/sagas.test.js +123 -193
  85. package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -72
  86. package/v2Containers/TemplatesV2/index.js +23 -86
  87. package/v2Containers/Whatsapp/index.js +20 -3
  88. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -578
  89. package/utils/rcsPayloadUtils.js +0 -92
  90. package/utils/templateVarUtils.js +0 -201
  91. package/utils/tests/templateVarUtils.test.js +0 -204
  92. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js.rej +0 -18
  93. package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
  94. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
  95. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -87
  96. package/v2Components/SmsFallback/constants.js +0 -73
  97. package/v2Components/SmsFallback/index.js +0 -955
  98. package/v2Components/SmsFallback/index.scss +0 -265
  99. package/v2Components/SmsFallback/messages.js +0 -78
  100. package/v2Components/SmsFallback/smsFallbackUtils.js +0 -118
  101. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
  102. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
  103. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
  104. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -197
  105. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -277
  106. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
  107. package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
  108. package/v2Components/TemplatePreview/constants.js +0 -2
  109. package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
  110. package/v2Components/VarSegmentMessageEditor/index.js +0 -125
  111. package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
  112. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -43
  113. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -67
  114. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
  115. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
  116. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
  117. package/v2Containers/Rcs/index.js.rej +0 -1336
  118. package/v2Containers/Rcs/index.scss.rej +0 -74
  119. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -225
  120. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap.rej +0 -128
  121. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -318
  122. package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
  123. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
  124. package/v2Containers/SmsTrai/Edit/index.scss +0 -121
  125. package/v2Containers/Templates/TemplatesActionBar.js +0 -101
  126. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
  127. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
  128. package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
  129. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +0 -131
@@ -1,5 +1,9 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
+ import {
4
+ CAP_SPACE_16, CAP_SPACE_32, CAP_SPACE_56, CAP_SPACE_64,
5
+ } from '@capillarytech/cap-ui-library/styled/variables';
6
+
3
7
  import CapSlideBox from '@capillarytech/cap-ui-library/CapSlideBox';
4
8
  import CapHeader from '@capillarytech/cap-ui-library/CapHeader';
5
9
  import CapRow from '@capillarytech/cap-ui-library/CapRow';
@@ -9,11 +13,12 @@ import CapNotification from '@capillarytech/cap-ui-library/CapNotification';
9
13
  import { injectIntl, FormattedMessage } from 'react-intl';
10
14
  import classnames from 'classnames';
11
15
  import {
12
- isEmpty, get, forEach, cloneDeep, debounce, pick,
16
+ isEmpty, get, forEach, cloneDeep, debounce,
13
17
  } from 'lodash';
14
18
  import { connect } from 'react-redux';
15
19
  import { createStructuredSelector } from 'reselect';
16
20
  import { bindActionCreators, compose } from 'redux';
21
+ import styled from 'styled-components';
17
22
  import { GA } from '@capillarytech/cap-ui-utils';
18
23
  import { DAEMON } from '@capillarytech/vulcan-react-sdk/utils/sagaInjectorTypes';
19
24
 
@@ -27,7 +32,6 @@ import SlideBoxContent from './SlideBoxContent';
27
32
  import * as constants from './constants';
28
33
  import * as commonUtil from '../../utils/common';
29
34
  import { gtmPush } from '../../utils/gtmTrackers';
30
- import { normalizeRcsMessageContentForApi } from '../../utils/rcsPayloadUtils';
31
35
  import './index.scss';
32
36
  import * as templateActions from '../Templates/actions';
33
37
  import * as globalActions from '../Cap/actions';
@@ -43,9 +47,6 @@ import {
43
47
  import {EXTERNAL_URL, SITE_URL, WEBPUSH_MEDIA_TYPES} from '../WebPush/constants';
44
48
  import { IMAGE, VIDEO } from '../Facebook/Advertisement/constant';
45
49
  import { RCS_STATUSES } from '../Rcs/constants';
46
- import { mapRcsCardContentForConsumerWithResolvedTags } from '../Rcs/utils';
47
- import { pickRcsCardVarMappedEntries } from '../Rcs/rcsLibraryHydrationUtils';
48
- import { RCS_SMS_FALLBACK_VAR_MAPPED_PROP } from '../../v2Components/CommonTestAndPreview/constants';
49
50
  import { CREATIVE } from '../Facebook/constants';
50
51
  import { LOYALTY } from '../App/constants';
51
52
  import {
@@ -60,11 +61,6 @@ import { capSagaForFetchSchemaForEntity, capSagaLiquidEntity } from '../Cap/saga
60
61
  import { v2TemplateSagaWatchGetDefaultBeeTemplates } from '../Templates/sagas';
61
62
  import { DYNAMIC_URL } from '../../v2Components/CapWhatsappCTA/constants';
62
63
  import ErrorInfoNote from '../../v2Components/ErrorInfoNote';
63
- import SlideBoxWrapper from './CreativesSlideBoxWrapper';
64
- import {
65
- computeLiquidFooterUpdateFromFormBuilder,
66
- getSlideBoxWrapperMarginFromLiquidErrors,
67
- } from './embeddedSlideboxUtils';
68
64
 
69
65
  import {
70
66
  transformChannelPayload,
@@ -73,24 +69,51 @@ import {
73
69
  import { MANUAL_CAROUSEL } from '../MobilePushNew/constants';
74
70
  import { BIG_HTML } from '../InApp/constants';
75
71
 
72
+ /**
73
+ * Returns true if value is "deep empty": no errors present.
74
+ * - null/undefined: empty
75
+ * - string: empty if length === 0
76
+ * - array: empty if length === 0
77
+ * - plain object (e.g. { android: [], ios: [], generic: [] }): empty only if every value is deep-empty
78
+ */
79
+ function isDeepEmpty(value) {
80
+ if (value == null) return true;
81
+ if (typeof value === 'string') return value.length === 0;
82
+ if (Array.isArray(value)) return value.length === 0;
83
+ if (typeof value === 'object') {
84
+ return Object.values(value).every(isDeepEmpty);
85
+ }
86
+ return false;
87
+ }
88
+
76
89
  const classPrefix = 'add-creatives-section';
77
90
  const CREATIVES_CONTAINER = 'creativesContainer';
78
91
 
92
+ const SlideBoxWrapper = styled.div`
93
+ .cap-slide-box-v2-container{
94
+ .slidebox-header, .slidebox-content-container{
95
+ margin-bottom: ${({ slideBoxWrapperMargin }) => `${slideBoxWrapperMargin}`};
96
+ padding: 0 rem;
97
+ &.has-footer{
98
+ overflow-x: hidden;
99
+ }
100
+ }
101
+ .slidebox-footer{
102
+ /* Only apply margin-bottom to footer when ErrorInfoNote is shown in footer (BEE editor) */
103
+ /* For HTML Editor, errors are shown in ValidationErrorDisplay (inside content area), so no footer margin needed */
104
+ margin-bottom: ${({ shouldApplyFooterMargin }) => (shouldApplyFooterMargin ? `${CAP_SPACE_16}` : '0')};
105
+ padding: 0 rem;
106
+ &.has-footer{
107
+ overflow-x: hidden;
108
+ }
109
+ }
110
+ }
111
+ `;
79
112
  export class Creatives extends React.Component {
80
113
  constructor(props) {
81
114
  super(props);
82
115
 
83
- const useLocalTemplates = get(
84
- props,
85
- 'localTemplatesConfig.useLocalTemplates',
86
- get(props, 'useLocalTemplates', false),
87
- );
88
- const initialSlidBoxContent = this.getSlideBoxContent({
89
- mode: props.creativesMode,
90
- templateData: props.templateData,
91
- isFullMode: props.isFullMode,
92
- useLocalTemplates,
93
- });
116
+ const initialSlidBoxContent = this.getSlideBoxContent({ mode: props.creativesMode, templateData: props.templateData, isFullMode: props.isFullMode });
94
117
 
95
118
  this.state = {
96
119
  isLoadingContent: true,
@@ -137,13 +160,7 @@ export class Creatives extends React.Component {
137
160
  }
138
161
 
139
162
  componentWillUnmount() {
140
- const isEmbedded = get(this.props, 'location.query.type', '') === "embedded";
141
- const useLocalTemplates = get(
142
- this.props,
143
- 'localTemplatesConfig.useLocalTemplates',
144
- get(this.props, 'useLocalTemplates', false),
145
- );
146
- if (isEmbedded && !useLocalTemplates) {
163
+ if (get(this.props, 'location.query.type', '') === "embedded") {
147
164
  this.props.templateActions.resetTemplateStoreData();
148
165
  }
149
166
  this.props.globalActions.clearMetaEntities();
@@ -745,56 +762,25 @@ export class Creatives extends React.Component {
745
762
  smsFallBackContent = {},
746
763
  creativeName = "",
747
764
  channel = constants.RCS,
748
- rcsCardVarMapped,
765
+ accountId = "",
749
766
  } = templateData || {};
750
- const { isFullMode: isFullModeForRcsPayload } = this.props;
751
- const firstCardIn = (rcsContent.cardContent && rcsContent.cardContent[0]) || {};
752
- const {
753
- cardDisplayTitle: _omitDispTitleIn,
754
- cardDisplayDescription: _omitDispDescIn,
755
- ...cardContent
756
- } = firstCardIn;
767
+ const cardContent = (rcsContent.cardContent && rcsContent.cardContent[0]) || {};
757
768
  const Status = RCS_STATUSES.approved || '';
758
- const mergedCardVarMapped = (() => {
759
- const nestedCardVarMapped = cardContent?.cardVarMapped;
760
- const rootMirrorCardVarMapped = rcsCardVarMapped;
761
- const nestedRecord =
762
- nestedCardVarMapped != null && typeof nestedCardVarMapped === 'object'
763
- ? nestedCardVarMapped
764
- : {};
765
- const rootRecord =
766
- rootMirrorCardVarMapped != null && typeof rootMirrorCardVarMapped === 'object'
767
- ? rootMirrorCardVarMapped
768
- : {};
769
- const mergedFromRootAndNested = {
770
- ...pickRcsCardVarMappedEntries(rootRecord),
771
- ...pickRcsCardVarMappedEntries(nestedRecord),
772
- };
773
- return Object.keys(mergedFromRootAndNested).length > 0
774
- ? mergedFromRootAndNested
775
- : null;
776
- })();
777
- // Campaigns (embedded): do not duplicate `cardVarMapped` as root `rcsCardVarMapped` on send —
778
- // slot map stays on `versions…cardContent[0].cardVarMapped` only. Library full mode keeps root mirror.
779
- // Use `=== true` so omitted/undefined `isFullMode` does not behave like library (avoids duplicate on approval payload).
780
- const includeRootRcsCardVarMapped =
781
- mergedCardVarMapped && isFullModeForRcsPayload === true;
782
769
 
783
770
  creativesTemplateData = {
784
771
  type: channel,
785
772
  edit: true,
786
773
  name: creativeName,
787
- ...(includeRootRcsCardVarMapped ? { rcsCardVarMapped: mergedCardVarMapped } : {}),
788
774
  versions: {
789
775
  base: {
790
776
  content: {
791
777
  RCS: {
792
778
  rcsContent: {
793
779
  ...rcsContent,
780
+ ...(accountId && !isFullMode && { accountId }),
794
781
  cardContent: [
795
782
  {
796
783
  ...cardContent,
797
- ...(mergedCardVarMapped ? { cardVarMapped: mergedCardVarMapped } : {}),
798
784
  Status,
799
785
  },
800
786
  ],
@@ -945,10 +931,7 @@ export class Creatives extends React.Component {
945
931
  return newExpandableDetails;
946
932
  }
947
933
 
948
- getCreativesData = async (channelParam, template, templateRecords) => { //from creatives to consumers
949
- const channel = String(
950
- channelParam || template?.type || get(template, 'value.type') || '',
951
- ).toUpperCase();
934
+ getCreativesData = async (channel, template, templateRecords) => { //from creatives to consumers
952
935
  let templateData = { channel };
953
936
  switch (channel) {
954
937
  case constants.SMS:
@@ -1242,7 +1225,7 @@ export class Creatives extends React.Component {
1242
1225
  };
1243
1226
  }
1244
1227
  break;
1245
- case constants.FACEBOOK: {
1228
+ case constants.FACEBOOK:
1246
1229
  if (template.value) {
1247
1230
  const FacebookAd = template?.value?.versions?.base?.content?.FacebookAd;
1248
1231
  const { type } = FacebookAd[0];
@@ -1286,110 +1269,34 @@ export class Creatives extends React.Component {
1286
1269
  selectedMarketingObjective: template.value.selectedMarketingObjective,
1287
1270
  };
1288
1271
  }
1289
- }
1290
1272
  break;
1291
- case constants.RCS: {
1273
+ case constants.RCS:
1292
1274
  if (template.value) {
1293
- const { isFullMode: isFullModeForRcsConsumerPayload } = this.props;
1294
- const { name = "", versions = {} } = template.value || {};
1295
- const fromSubmit = get(versions, 'base.content.RCS.smsFallBackContent', {});
1296
- const fromRecords = {
1297
- ...(templateRecords?.smsFallBackContent || {}),
1298
- ...(get(templateRecords, 'versions.base.content.RCS.smsFallBackContent') || {}),
1299
- };
1300
- const hasMeaningfulRcsSmsFallback = (smsFallbackPayload) => {
1301
- if (
1302
- !smsFallbackPayload
1303
- || typeof smsFallbackPayload !== 'object'
1304
- || Object.keys(smsFallbackPayload).length === 0
1305
- ) {
1306
- return false;
1307
- }
1308
- const fallbackBodyText = String(
1309
- smsFallbackPayload.smsContent
1310
- ?? smsFallbackPayload.smsTemplateContent
1311
- ?? smsFallbackPayload.message
1312
- ?? smsFallbackPayload.content
1313
- ?? '',
1314
- ).trim();
1315
- const fallbackTemplateName = String(
1316
- smsFallbackPayload.smsTemplateName ?? smsFallbackPayload.templateName ?? '',
1317
- ).trim();
1318
- const rcsSmsFallbackVarMapped =
1319
- smsFallbackPayload?.[RCS_SMS_FALLBACK_VAR_MAPPED_PROP];
1320
- const hasVarMappedEntries =
1321
- rcsSmsFallbackVarMapped != null
1322
- && typeof rcsSmsFallbackVarMapped === 'object'
1323
- && Object.keys(rcsSmsFallbackVarMapped).length > 0;
1324
- return (
1325
- fallbackBodyText !== ''
1326
- || fallbackTemplateName !== ''
1327
- || hasVarMappedEntries
1328
- );
1329
- };
1330
- // If submit has only empty strings, do not let it wipe fallback mirrored on templateRecords (library round-trip).
1331
- const smsFallBackContent = hasMeaningfulRcsSmsFallback(fromSubmit)
1332
- ? { ...fromRecords, ...fromSubmit }
1333
- : { ...fromSubmit, ...fromRecords };
1275
+ const { name = "", versions = {} } = {
1276
+ } = template.value || {};
1277
+ const smsFallBackContent = get(versions, 'base.content.RCS.smsFallBackContent', {});
1334
1278
  const {
1335
- cardContent: cardContentFromSubmit = [],
1279
+ cardContent = [],
1336
1280
  contentType = "",
1337
1281
  cardType = "",
1338
1282
  cardSettings = {},
1283
+ accountId = "",
1339
1284
  } = get(versions, 'base.content.RCS.rcsContent', {});
1340
- const rootRcsCardVarMappedFromSubmit = get(template, 'value.rcsCardVarMapped');
1341
- const firstCardFromSubmit = Array.isArray(cardContentFromSubmit)
1342
- ? cardContentFromSubmit[0]
1343
- : null;
1344
- const cardContent = mapRcsCardContentForConsumerWithResolvedTags(
1345
- cardContentFromSubmit,
1346
- rootRcsCardVarMappedFromSubmit,
1347
- isFullModeForRcsConsumerPayload,
1348
- );
1349
1285
  const rcsContent = {
1350
1286
  contentType,
1351
1287
  cardType,
1352
1288
  cardSettings,
1353
1289
  cardContent,
1354
1290
  };
1355
- const cardVarMappedFromFirstRcsCard =
1356
- firstCardFromSubmit?.cardVarMapped != null
1357
- && typeof firstCardFromSubmit.cardVarMapped === 'object'
1358
- ? pickRcsCardVarMappedEntries(firstCardFromSubmit.cardVarMapped)
1359
- : null;
1360
- const includeRootRcsCardVarMappedOnConsumerPayload =
1361
- cardVarMappedFromFirstRcsCard
1362
- && Object.keys(cardVarMappedFromFirstRcsCard).length > 0
1363
- && isFullModeForRcsConsumerPayload === true;
1364
1291
  templateData = {
1365
1292
  channel,
1366
1293
  creativeName: name,
1367
1294
  rcsContent,
1368
- ...(includeRootRcsCardVarMappedOnConsumerPayload
1369
- ? { rcsCardVarMapped: cardVarMappedFromFirstRcsCard }
1370
- : {}),
1295
+ accountId,
1371
1296
  };
1372
- // Library / campaign consumers round-trip templateData via getTemplateData; include SMS fallback
1373
- // so reopening the editor restores fallback text and tag mappings.
1374
- // cap-campaigns-v2 API expects `smsFallBackContent.message` (see normalizeRcsMessageContentForApi).
1375
- if (hasMeaningfulRcsSmsFallback(smsFallBackContent)) {
1376
- const smsText =
1377
- smsFallBackContent.message
1378
- ?? smsFallBackContent.smsContent
1379
- ?? smsFallBackContent.smsTemplateContent
1380
- ?? '';
1381
- templateData.smsFallBackContent = {
1382
- ...smsFallBackContent,
1383
- ...(String(smsText).trim() !== ''
1384
- ? { message: String(smsText).trim() }
1385
- : {}),
1386
- };
1387
- }
1388
- normalizeRcsMessageContentForApi(templateData);
1389
1297
  }
1390
- }
1391
1298
  break;
1392
- case constants.ZALO: {
1299
+ case constants.ZALO:
1393
1300
  if (template.value) {
1394
1301
  templateData = {
1395
1302
  ...template.value,
@@ -1398,7 +1305,6 @@ export class Creatives extends React.Component {
1398
1305
  delete templateData.type;
1399
1306
  }
1400
1307
  }
1401
- }
1402
1308
  break;
1403
1309
  case constants.WEBPUSH: {
1404
1310
  if (template.value) {
@@ -1505,10 +1411,7 @@ export class Creatives extends React.Component {
1505
1411
  return templateData;
1506
1412
  };
1507
1413
 
1508
- getSlideBoxContent({ mode, templateData, isFullMode, useLocalTemplates }) {
1509
- if (useLocalTemplates && mode === 'create' && isEmpty(templateData)) {
1510
- return 'templates';
1511
- }
1414
+ getSlideBoxContent({ mode, templateData, isFullMode }) {
1512
1415
  let creativesMode = isFullMode ? 'createTemplate' : 'templates';// for library mode templates page is initial mode and for full mode createTemplates
1513
1416
  if (mode === 'create' && isFullMode) {
1514
1417
  creativesMode = 'createTemplate';
@@ -1596,110 +1499,24 @@ export class Creatives extends React.Component {
1596
1499
  getFormData = (template) => {
1597
1500
  // Always reset isGetFormData so the child does not re-send form data on every re-render
1598
1501
  // (e.g. when user fixes validation error by typing, we must not auto-close the slidebox)
1599
- this.setState(
1600
- (prevState) => {
1601
- const next = { isGetFormData: false };
1602
- if (!template.validity) {
1603
- return next;
1604
- }
1605
- const baseTd = prevState.templateData || template;
1606
- const channel = (
1607
- baseTd?.type
1608
- || template?.type
1609
- || get(template, 'value.type')
1610
- || ''
1611
- ).toUpperCase();
1612
- // Library mode: persist last submitted creatives shape so reopening still hydrates the editor
1613
- // (parent may not merge getCreativesData back into templateData).
1614
- if (this.props.isFullMode === false && template.value) {
1615
- if (channel === constants.RCS) {
1616
- const smsFallBackFromPayload = get(
1617
- template.value,
1618
- 'versions.base.content.RCS.smsFallBackContent',
1619
- );
1620
- const rcsCardVarMappedFromPayload = get(
1621
- template.value,
1622
- 'versions.base.content.RCS.rcsContent.cardContent[0].cardVarMapped',
1623
- );
1624
- next.templateData = {
1625
- ...baseTd,
1626
- type: constants.RCS,
1627
- name: template?.value?.name,
1628
- versions: template?.value?.versions,
1629
- ...(smsFallBackFromPayload != null
1630
- && typeof smsFallBackFromPayload === 'object'
1631
- && Object.keys(smsFallBackFromPayload).length > 0
1632
- ? { smsFallBackContent: smsFallBackFromPayload }
1633
- : {}),
1634
- ...(rcsCardVarMappedFromPayload != null
1635
- && typeof rcsCardVarMappedFromPayload === 'object'
1636
- ? { rcsCardVarMapped: rcsCardVarMappedFromPayload }
1637
- : {}),
1638
- };
1639
- if (template._id) {
1640
- next.templateData._id = template._id;
1641
- }
1642
- } else if (channel === constants.SMS) {
1643
- const submittedSmsTemplateValue = template?.value;
1644
- const smsVersions =
1645
- submittedSmsTemplateValue?.history != null
1646
- ? submittedSmsTemplateValue
1647
- : {
1648
- base: submittedSmsTemplateValue?.base,
1649
- history: submittedSmsTemplateValue?.base
1650
- ? [submittedSmsTemplateValue.base]
1651
- : [],
1652
- };
1653
- next.templateData = {
1654
- ...baseTd,
1655
- type: constants.SMS,
1656
- name: baseTd?.name || 'Campaign message SMS content',
1657
- versions: smsVersions,
1658
- };
1659
- if (template?._id) {
1660
- next.templateData._id = template._id;
1661
- }
1662
- }
1663
- }
1664
- return next;
1665
- },
1666
- () => {
1667
- if (!template.validity) {
1668
- return;
1669
- }
1670
- const templateData = this.state.templateData ? this.state.templateData : template; //select existing or create new content
1671
- const channelForConsumer = String(
1672
- templateData.type
1673
- || template.type
1674
- || get(template, 'value.type')
1675
- || '',
1676
- ).toUpperCase();
1677
- const creativesData = this.getCreativesData(
1678
- channelForConsumer,
1679
- template,
1680
- this.state.templateData || template,
1681
- );// convers data to consumer understandable format
1682
- creativesData.then((data) => {
1683
- this.logGTMEvent(channelForConsumer, data);
1684
- this.processCentralCommsMetaId(channelForConsumer, data, {
1685
- closeSlideBoxAfterSubmit: template.closeSlideBoxAfterSubmit,
1502
+ this.setState({ isGetFormData: false });
1503
+ if (template.validity) {
1504
+ this.setState(
1505
+ {},
1506
+ () => {
1507
+ const templateData = this.state.templateData ? this.state.templateData : template; //select existing or create new content
1508
+ const channel = templateData.type;
1509
+ const creativesData = this.getCreativesData(channel, template, templateData);// convers data to consumer understandable format
1510
+ creativesData.then((data) => {
1511
+ this.logGTMEvent(channel, data);
1512
+ this.processCentralCommsMetaId(channel, data);
1686
1513
  });
1687
- });
1688
- },
1689
- );
1514
+ },
1515
+ );
1516
+ }
1690
1517
  }
1691
1518
 
1692
- processCentralCommsMetaId = (channel, creativesData, options = {}) => {
1693
- const { closeSlideBoxAfterSubmit = false } = options;
1694
- const maybeCloseLibrarySlideBox = () => {
1695
- if (
1696
- closeSlideBoxAfterSubmit
1697
- && this.props.isFullMode === false
1698
- && typeof this.handleCloseSlideBox === 'function'
1699
- ) {
1700
- this.handleCloseSlideBox();
1701
- }
1702
- };
1519
+ processCentralCommsMetaId = (channel, creativesData) => {
1703
1520
  // Create the payload for the centralcommnsmetaId API call
1704
1521
  const { isLoyaltyModule = false, loyaltyMetaData = {} } = this.props;
1705
1522
  const { actionName, setMetaData = () => { } } = loyaltyMetaData;
@@ -1725,7 +1542,6 @@ export class Creatives extends React.Component {
1725
1542
  if (result?.status?.code === 200) {
1726
1543
  setMetaData(result);
1727
1544
  this.props.getCreativesData(creativesData);
1728
- maybeCloseLibrarySlideBox();
1729
1545
  } else {
1730
1546
  CapNotification.error({ message: <FormattedMessage {...messages.somethingWentWrong} /> });
1731
1547
  }
@@ -1736,7 +1552,6 @@ export class Creatives extends React.Component {
1736
1552
  } else {
1737
1553
  // If not a loyalty module or different action, should work as usual
1738
1554
  this.props.getCreativesData(creativesData);
1739
- maybeCloseLibrarySlideBox();
1740
1555
  }
1741
1556
  };
1742
1557
 
@@ -1769,9 +1584,7 @@ export class Creatives extends React.Component {
1769
1584
  }
1770
1585
  this.setState((prevState) => ({
1771
1586
  ...prevState,
1772
- // Library mode (isFullMode === false): retain last template so reopening still has RCS payload.
1773
- // Undefined isFullMode defaults to full-mode close behavior (clear templateData).
1774
- ...(this.props.isFullMode !== false ? { templateData: undefined } : {}),
1587
+ templateData: undefined,
1775
1588
  showSlideBox: false,
1776
1589
  liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
1777
1590
  isLiquidValidationError: false,
@@ -1982,12 +1795,21 @@ export class Creatives extends React.Component {
1982
1795
  }
1983
1796
 
1984
1797
  showLiquidErrorInFooter = (errorMessagesFromFormBuilder, currentFormBuilderTab) => {
1985
- const next = computeLiquidFooterUpdateFromFormBuilder(errorMessagesFromFormBuilder, currentFormBuilderTab, {
1986
- previousIsLiquidValidationError: this.state.isLiquidValidationError,
1987
- currentChannelUpper: this.state.currentChannel?.toUpperCase(),
1798
+ const liquidMsgs = get(errorMessagesFromFormBuilder, constants.LIQUID_ERROR_MSG, []);
1799
+ const standardMsgs = get(errorMessagesFromFormBuilder, constants.STANDARD_ERROR_MSG, []);
1800
+ const hasLiquid = !isDeepEmpty(liquidMsgs);
1801
+ const hasStandard = !isDeepEmpty(standardMsgs);
1802
+ const isLiquidValidationError = hasLiquid || hasStandard;
1803
+ // Don't overwrite existing liquid error with empty only for Mobile Push OLD (FormBuilder/clear calls empty there); SMS/others clear on input change
1804
+ const isMobilePush = this.state.currentChannel?.toUpperCase() === constants.MOBILE_PUSH;
1805
+ if (!hasLiquid && !hasStandard && this.state.isLiquidValidationError && isMobilePush) {
1806
+ return;
1807
+ }
1808
+ this.setState({
1809
+ isLiquidValidationError,
1810
+ liquidErrorMessage: errorMessagesFromFormBuilder,
1811
+ activeFormBuilderTab: currentFormBuilderTab === 1 ? constants.ANDROID : (currentFormBuilderTab === 2 ? constants.IOS : null), // Update activeFormBuilderTab, default to 1 if undefined
1988
1812
  });
1989
- if (next == null) return;
1990
- this.setState(next);
1991
1813
  }
1992
1814
 
1993
1815
  // Callback to update HTML Editor validation state (called from EmailWrapper)
@@ -2110,11 +1932,6 @@ export class Creatives extends React.Component {
2110
1932
  inAppEditorType,
2111
1933
  htmlEditorValidationState,
2112
1934
  } = this.state;
2113
- const useLocalTemplates = get(
2114
- this.props,
2115
- 'localTemplatesConfig.useLocalTemplates',
2116
- get(this.props, 'useLocalTemplates', false),
2117
- );
2118
1935
  const {
2119
1936
  isFullMode,
2120
1937
  creativesMode,
@@ -2133,6 +1950,7 @@ export class Creatives extends React.Component {
2133
1950
  smsRegister,
2134
1951
  enableNewChannels,
2135
1952
  eventContextTags,
1953
+ waitEventContextTags = {},
2136
1954
  isLoyaltyModule,
2137
1955
  loyaltyMetaData = {},
2138
1956
  } = this.props;
@@ -2166,7 +1984,14 @@ export class Creatives extends React.Component {
2166
1984
  // IMPORTANT: Never show ErrorInfoNote in footer when in HTML Editor mode, even if liquidErrorMessage exists
2167
1985
  const shouldShowErrorInfoNoteInFooter = isHTMLEditorMode ? false : hasBEEEditorErrors;
2168
1986
 
2169
- const slideBoxWrapperMargin = getSlideBoxWrapperMarginFromLiquidErrors(liquidErrorMessage);
1987
+ // Calculate margin for header/content (always apply if there are errors, regardless of editor type)
1988
+ const slideBoxWrapperMargin = (get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0 && get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0)
1989
+ ? CAP_SPACE_64
1990
+ : get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0
1991
+ ? CAP_SPACE_56
1992
+ : get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0
1993
+ ? CAP_SPACE_32
1994
+ : 0;
2170
1995
  /* TODO: Instead of passing down same props separately to each component down, write common function to these props and pass it accordingly */
2171
1996
 
2172
1997
  // Compute anonymous user type and channel restrictions
@@ -2195,10 +2020,7 @@ export class Creatives extends React.Component {
2195
2020
  <SlideBoxWrapper
2196
2021
  slideBoxWrapperMargin={slideBoxWrapperMargin}
2197
2022
  shouldApplyFooterMargin={shouldShowErrorInfoNoteInFooter}
2198
- className={classnames(
2199
- `${classPrefix} ${isFullMode ? 'creatives-full-mode' : 'creatives-library-mode'} ${mapTemplateCreate ? 'map-template-create' : ''}`,
2200
- useLocalTemplates && slidBoxContent === 'templates' && 'creatives-slidebox--local-sms-templates',
2201
- )}
2023
+ className={classnames(`${classPrefix} ${isFullMode ? 'creatives-full-mode' : 'creatives-library-mode'} ${mapTemplateCreate ? 'map-template-create' : ''}`)}
2202
2024
  >
2203
2025
  <CapSlideBox
2204
2026
  header={
@@ -2223,13 +2045,12 @@ export class Creatives extends React.Component {
2223
2045
  smsRegister={smsRegister}
2224
2046
  handleClose={this.handleCloseSlideBox}
2225
2047
  moduleType={this.props.messageDetails?.type}
2226
- useLocalTemplates={useLocalTemplates}
2227
2048
  />
2228
2049
  )}
2229
2050
  content={(
2230
2051
  <SlideBoxContent
2231
2052
  key="creatives-container-slidebox-content"
2232
- onSelectTemplate={this.props.onSelectTemplate != null ? this.props.onSelectTemplate : this.onSelectTemplate}
2053
+ onSelectTemplate={this.onSelectTemplate}
2233
2054
  onCreateComplete={getCreativesData}
2234
2055
  onPreviewTemplate={this.onPreviewTemplate}
2235
2056
  slidBoxContent={slidBoxContent}
@@ -2297,6 +2118,7 @@ export class Creatives extends React.Component {
2297
2118
  creativesMode={creativesMode} // An existing prop that we're using here. Required to ensure correct account details in Edit or Preview in case of Embedded mode.
2298
2119
  hostName={this.props?.hostName || ''}
2299
2120
  eventContextTags={eventContextTags}
2121
+ waitEventContextTags={waitEventContextTags}
2300
2122
  isLoyaltyModule={isLoyaltyModule}
2301
2123
  loyaltyMetaData={loyaltyMetaData}
2302
2124
  showTestAndPreviewSlidebox={showTestAndPreviewSlidebox}
@@ -2305,8 +2127,7 @@ export class Creatives extends React.Component {
2305
2127
  isTestAndPreviewMode={(() => this.state.isTestAndPreviewMode)()}
2306
2128
  onHtmlEditorValidationStateChange={this.updateHtmlEditorValidationState}
2307
2129
  onPersonalizationTokensChange={this.handlePersonalizationTokensChange}
2308
- localTemplatesConfig={pick(this.props.localTemplatesConfig || this.props, constants.LOCAL_TEMPLATE_CONFIG_KEYS)}
2309
- />
2130
+ />
2310
2131
  )}
2311
2132
  footer={this.shouldShowFooter() ? (
2312
2133
  <SlideBoxFooter
@@ -2390,31 +2211,13 @@ Creatives.propTypes = {
2390
2211
  orgUnitId: PropTypes.number,
2391
2212
  hostName: PropTypes.string,
2392
2213
  eventContextTags: PropTypes.array,
2214
+ waitEventContextTags: PropTypes.object,
2393
2215
  loyaltyTagFetchingDependencies: PropTypes.object,
2394
2216
  customerType: PropTypes.string,
2395
2217
  intl: PropTypes.shape({
2396
2218
  formatMessage: PropTypes.func,
2397
2219
  }),
2398
2220
  stopValidation: PropTypes.func,
2399
- // Local template list (e.g. for SMS fallback): when set, TemplatesV2 uses these instead of Redux.
2400
- // All optional. Pass either localTemplatesConfig (object) or individual props below.
2401
- localTemplatesConfig: PropTypes.shape({
2402
- useLocalTemplates: PropTypes.bool,
2403
- localTemplates: PropTypes.arrayOf(PropTypes.object),
2404
- localTemplatesLoading: PropTypes.bool,
2405
- localTemplatesFilterContent: PropTypes.node,
2406
- localTemplatesSentinelContent: PropTypes.node,
2407
- localTemplatesScrollContainerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
2408
- localTemplatesUseSkeleton: PropTypes.bool,
2409
- }),
2410
- useLocalTemplates: PropTypes.bool,
2411
- localTemplates: PropTypes.arrayOf(PropTypes.object),
2412
- localTemplatesLoading: PropTypes.bool,
2413
- localTemplatesFilterContent: PropTypes.node,
2414
- localTemplatesSentinelContent: PropTypes.node,
2415
- localTemplatesScrollContainerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
2416
- localTemplatesUseSkeleton: PropTypes.bool,
2417
- onSelectTemplate: PropTypes.func,
2418
2221
  };
2419
2222
  const mapStatesToProps = () => createStructuredSelector({
2420
2223
  isLoading: isLoadingSelector(),