@capillarytech/creatives-library 8.0.353-alpha.6 → 8.0.354

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 (127) hide show
  1. package/constants/unified.js +0 -29
  2. package/index.html +1 -0
  3. package/package.json +1 -1
  4. package/services/tests/api.test.js +20 -35
  5. package/utils/cdnTransformation.js +63 -3
  6. package/utils/commonUtils.js +1 -19
  7. package/utils/tests/cdnTransformation.test.js +111 -0
  8. package/v2Components/CapActionButton/constants.js +0 -7
  9. package/v2Components/CapActionButton/index.js +108 -166
  10. package/v2Components/CapActionButton/index.scss +6 -157
  11. package/v2Components/CapActionButton/messages.js +3 -19
  12. package/v2Components/CapActionButton/tests/index.test.js +17 -41
  13. package/v2Components/CapTagList/index.js +0 -10
  14. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +49 -72
  15. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
  16. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -213
  17. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
  18. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +10 -85
  19. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
  20. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -79
  21. package/v2Components/CommonTestAndPreview/SendTestMessage.js +5 -10
  22. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +15 -157
  23. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +76 -346
  24. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +4 -133
  25. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +0 -11
  26. package/v2Components/CommonTestAndPreview/constants.js +2 -38
  27. package/v2Components/CommonTestAndPreview/index.js +186 -691
  28. package/v2Components/CommonTestAndPreview/messages.js +3 -45
  29. package/v2Components/CommonTestAndPreview/sagas.js +6 -25
  30. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +284 -308
  31. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +65 -231
  32. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +5 -118
  33. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -341
  34. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +1 -8
  35. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +13 -34
  36. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +283 -281
  37. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
  38. package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -132
  39. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +26 -36
  40. package/v2Components/FormBuilder/index.js +6 -11
  41. package/v2Components/TemplatePreview/_templatePreview.scss +23 -38
  42. package/v2Components/TemplatePreview/index.js +31 -143
  43. package/v2Components/TemplatePreview/tests/index.test.js +0 -142
  44. package/v2Components/TestAndPreviewSlidebox/index.js +1 -13
  45. package/v2Components/TestAndPreviewSlidebox/sagas.js +4 -11
  46. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +1 -3
  47. package/v2Containers/CreativesContainer/SlideBoxContent.js +4 -36
  48. package/v2Containers/CreativesContainer/SlideBoxFooter.js +1 -10
  49. package/v2Containers/CreativesContainer/SlideBoxHeader.js +4 -29
  50. package/v2Containers/CreativesContainer/constants.js +0 -9
  51. package/v2Containers/CreativesContainer/index.js +103 -322
  52. package/v2Containers/CreativesContainer/index.scss +1 -51
  53. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -78
  54. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
  55. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
  56. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +98 -357
  57. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +15 -20
  58. package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
  59. package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
  60. package/v2Containers/Rcs/constants.js +10 -119
  61. package/v2Containers/Rcs/index.js +818 -2450
  62. package/v2Containers/Rcs/index.scss +8 -280
  63. package/v2Containers/Rcs/messages.js +3 -34
  64. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +70073 -98018
  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 -106
  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 -640
  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 +9 -166
  79. package/v2Containers/Templates/actions.js +0 -11
  80. package/v2Containers/Templates/constants.js +0 -2
  81. package/v2Containers/Templates/index.js +52 -120
  82. package/v2Containers/Templates/sagas.js +18 -57
  83. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1017 -1062
  84. package/v2Containers/Templates/tests/sagas.test.js +39 -205
  85. package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -72
  86. package/v2Containers/TemplatesV2/index.js +23 -86
  87. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
  88. package/v2Containers/Whatsapp/index.js +20 -3
  89. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -578
  90. package/utils/rcsPayloadUtils.js +0 -92
  91. package/utils/templateVarUtils.js +0 -201
  92. package/utils/tests/rcsPayloadUtils.test.js +0 -226
  93. package/utils/tests/templateVarUtils.test.js +0 -204
  94. package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
  95. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
  96. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -91
  97. package/v2Components/SmsFallback/constants.js +0 -73
  98. package/v2Components/SmsFallback/index.js +0 -956
  99. package/v2Components/SmsFallback/index.scss +0 -265
  100. package/v2Components/SmsFallback/messages.js +0 -78
  101. package/v2Components/SmsFallback/smsFallbackUtils.js +0 -119
  102. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
  103. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
  104. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
  105. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -223
  106. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -309
  107. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
  108. package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
  109. package/v2Components/TemplatePreview/constants.js +0 -2
  110. package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
  111. package/v2Components/VarSegmentMessageEditor/index.js +0 -125
  112. package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
  113. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -43
  114. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -79
  115. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
  116. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
  117. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
  118. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -225
  119. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -318
  120. package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
  121. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
  122. package/v2Containers/SmsTrai/Edit/index.scss +0 -121
  123. package/v2Containers/Templates/TemplatesActionBar.js +0 -101
  124. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
  125. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
  126. package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
  127. 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();
@@ -751,69 +768,15 @@ export class Creatives extends React.Component {
751
768
  smsFallBackContent = {},
752
769
  creativeName = "",
753
770
  channel = constants.RCS,
754
- rcsCardVarMapped,
755
771
  accountId = "",
756
772
  } = templateData || {};
757
- const { isFullMode: isFullModeForRcsPayload } = this.props;
758
- const isCarouselRcs = (rcsContent?.cardType || '').toString().toLowerCase() === 'carousel';
759
- const firstCardIn = (rcsContent.cardContent && rcsContent.cardContent[0]) || {};
760
- const {
761
- cardDisplayTitle: _omitDispTitleIn,
762
- cardDisplayDescription: _omitDispDescIn,
763
- ...cardContent
764
- } = firstCardIn;
773
+ const cardContent = (rcsContent.cardContent && rcsContent.cardContent[0]) || {};
765
774
  const Status = RCS_STATUSES.approved || '';
766
- const mergedCardVarMapped = (() => {
767
- const nestedCardVarMapped = cardContent?.cardVarMapped;
768
- const rootMirrorCardVarMapped = rcsCardVarMapped;
769
- const nestedRecord =
770
- nestedCardVarMapped != null && typeof nestedCardVarMapped === 'object'
771
- ? nestedCardVarMapped
772
- : {};
773
- const rootRecord =
774
- rootMirrorCardVarMapped != null && typeof rootMirrorCardVarMapped === 'object'
775
- ? rootMirrorCardVarMapped
776
- : {};
777
- const mergedFromRootAndNested = {
778
- ...pickRcsCardVarMappedEntries(rootRecord),
779
- ...pickRcsCardVarMappedEntries(nestedRecord),
780
- };
781
- return Object.keys(mergedFromRootAndNested).length > 0
782
- ? mergedFromRootAndNested
783
- : null;
784
- })();
785
- // Campaigns (embedded): do not duplicate `cardVarMapped` as root `rcsCardVarMapped` on send —
786
- // slot map stays on `versions…cardContent[0].cardVarMapped` only. Library full mode keeps root mirror.
787
- // Use `=== true` so omitted/undefined `isFullMode` does not behave like library (avoids duplicate on approval payload).
788
- const includeRootRcsCardVarMapped =
789
- mergedCardVarMapped && isFullModeForRcsPayload === true;
790
-
791
- const builtCardContent = isCarouselRcs
792
- ? (rcsContent.cardContent || []).map((card, idx) => {
793
- const {
794
- cardDisplayTitle: _dt,
795
- cardDisplayDescription: _dd,
796
- ...restCard
797
- } = card || {};
798
- return {
799
- ...restCard,
800
- ...(idx === 0 && mergedCardVarMapped ? { cardVarMapped: mergedCardVarMapped } : {}),
801
- Status,
802
- };
803
- })
804
- : [
805
- {
806
- ...cardContent,
807
- ...(mergedCardVarMapped ? { cardVarMapped: mergedCardVarMapped } : {}),
808
- Status,
809
- },
810
- ];
811
775
 
812
776
  creativesTemplateData = {
813
777
  type: channel,
814
778
  edit: true,
815
779
  name: creativeName,
816
- ...(includeRootRcsCardVarMapped ? { rcsCardVarMapped: mergedCardVarMapped } : {}),
817
780
  versions: {
818
781
  base: {
819
782
  content: {
@@ -821,7 +784,12 @@ export class Creatives extends React.Component {
821
784
  rcsContent: {
822
785
  ...rcsContent,
823
786
  ...(accountId && !isFullMode && { accountId }),
824
- cardContent: builtCardContent,
787
+ cardContent: [
788
+ {
789
+ ...cardContent,
790
+ Status,
791
+ },
792
+ ],
825
793
  },
826
794
  smsFallBackContent,
827
795
  },
@@ -969,10 +937,7 @@ export class Creatives extends React.Component {
969
937
  return newExpandableDetails;
970
938
  }
971
939
 
972
- getCreativesData = async (channelParam, template, templateRecords) => { //from creatives to consumers
973
- const channel = String(
974
- channelParam || template?.type || get(template, 'value.type') || '',
975
- ).toUpperCase();
940
+ getCreativesData = async (channel, template, templateRecords) => { //from creatives to consumers
976
941
  let templateData = { channel };
977
942
  switch (channel) {
978
943
  case constants.SMS:
@@ -1319,104 +1284,28 @@ export class Creatives extends React.Component {
1319
1284
  break;
1320
1285
  case constants.RCS:
1321
1286
  if (template.value) {
1322
- const { isFullMode: isFullModeForRcsConsumerPayload } = this.props;
1323
- const { name = "", versions = {} } = template.value || {};
1324
- const fromSubmit = get(versions, 'base.content.RCS.smsFallBackContent', {});
1325
- const fromRecords = {
1326
- ...(templateRecords?.smsFallBackContent || {}),
1327
- ...(get(templateRecords, 'versions.base.content.RCS.smsFallBackContent') || {}),
1328
- };
1329
- const hasMeaningfulRcsSmsFallback = (smsFallbackPayload) => {
1330
- if (
1331
- !smsFallbackPayload
1332
- || typeof smsFallbackPayload !== 'object'
1333
- || Object.keys(smsFallbackPayload).length === 0
1334
- ) {
1335
- return false;
1336
- }
1337
- const fallbackBodyText = String(
1338
- smsFallbackPayload.smsContent
1339
- ?? smsFallbackPayload.smsTemplateContent
1340
- ?? smsFallbackPayload.message
1341
- ?? smsFallbackPayload.content
1342
- ?? '',
1343
- ).trim();
1344
- const fallbackTemplateName = String(
1345
- smsFallbackPayload.smsTemplateName ?? smsFallbackPayload.templateName ?? '',
1346
- ).trim();
1347
- const rcsSmsFallbackVarMapped =
1348
- smsFallbackPayload?.[RCS_SMS_FALLBACK_VAR_MAPPED_PROP];
1349
- const hasVarMappedEntries =
1350
- rcsSmsFallbackVarMapped != null
1351
- && typeof rcsSmsFallbackVarMapped === 'object'
1352
- && Object.keys(rcsSmsFallbackVarMapped).length > 0;
1353
- return (
1354
- fallbackBodyText !== ''
1355
- || fallbackTemplateName !== ''
1356
- || hasVarMappedEntries
1357
- );
1358
- };
1359
- // If submit has only empty strings, do not let it wipe fallback mirrored on templateRecords (library round-trip).
1360
- const smsFallBackContent = hasMeaningfulRcsSmsFallback(fromSubmit)
1361
- ? { ...fromRecords, ...fromSubmit }
1362
- : { ...fromSubmit, ...fromRecords };
1287
+ const { name = "", versions = {} } = {
1288
+ } = template.value || {};
1289
+ const smsFallBackContent = get(versions, 'base.content.RCS.smsFallBackContent', {});
1363
1290
  const {
1364
- cardContent: cardContentFromSubmit = [],
1291
+ cardContent = [],
1365
1292
  contentType = "",
1366
1293
  cardType = "",
1367
1294
  cardSettings = {},
1368
1295
  accountId = "",
1369
1296
  } = get(versions, 'base.content.RCS.rcsContent', {});
1370
- const rootRcsCardVarMappedFromSubmit = get(template, 'value.rcsCardVarMapped');
1371
- const firstCardFromSubmit = Array.isArray(cardContentFromSubmit)
1372
- ? cardContentFromSubmit[0]
1373
- : null;
1374
- const cardContent = mapRcsCardContentForConsumerWithResolvedTags(
1375
- cardContentFromSubmit,
1376
- rootRcsCardVarMappedFromSubmit,
1377
- isFullModeForRcsConsumerPayload,
1378
- );
1379
1297
  const rcsContent = {
1380
1298
  contentType,
1381
1299
  cardType,
1382
1300
  cardSettings,
1383
1301
  cardContent,
1384
1302
  };
1385
- const cardVarMappedFromFirstRcsCard =
1386
- firstCardFromSubmit?.cardVarMapped != null
1387
- && typeof firstCardFromSubmit.cardVarMapped === 'object'
1388
- ? pickRcsCardVarMappedEntries(firstCardFromSubmit.cardVarMapped)
1389
- : null;
1390
- const includeRootRcsCardVarMappedOnConsumerPayload =
1391
- cardVarMappedFromFirstRcsCard
1392
- && Object.keys(cardVarMappedFromFirstRcsCard).length > 0
1393
- && isFullModeForRcsConsumerPayload === true;
1394
1303
  templateData = {
1395
1304
  channel,
1396
1305
  creativeName: name,
1397
1306
  rcsContent,
1398
1307
  accountId,
1399
- ...(includeRootRcsCardVarMappedOnConsumerPayload
1400
- ? { rcsCardVarMapped: cardVarMappedFromFirstRcsCard }
1401
- : {}),
1402
1308
  };
1403
- // Library / campaign consumers round-trip templateData via getTemplateData; include SMS fallback
1404
- // so reopening the editor restores fallback text and tag mappings.
1405
- // cap-campaigns-v2 API expects `smsFallBackContent.message` (see normalizeRcsMessageContentForApi).
1406
- if (hasMeaningfulRcsSmsFallback(smsFallBackContent)) {
1407
- const smsText =
1408
- smsFallBackContent.message
1409
- ?? smsFallBackContent.smsContent
1410
- ?? smsFallBackContent.smsTemplateContent
1411
- ?? '';
1412
- templateData.smsFallBackContent = {
1413
- ...smsFallBackContent,
1414
- ...(String(smsText).trim() !== ''
1415
- ? { message: String(smsText).trim() }
1416
- : {}),
1417
- };
1418
- }
1419
- normalizeRcsMessageContentForApi(templateData);
1420
1309
  }
1421
1310
  break;
1422
1311
  case constants.ZALO:
@@ -1534,10 +1423,7 @@ export class Creatives extends React.Component {
1534
1423
  return templateData;
1535
1424
  };
1536
1425
 
1537
- getSlideBoxContent({ mode, templateData, isFullMode, useLocalTemplates }) {
1538
- if (useLocalTemplates && mode === 'create' && isEmpty(templateData)) {
1539
- return 'templates';
1540
- }
1426
+ getSlideBoxContent({ mode, templateData, isFullMode }) {
1541
1427
  let creativesMode = isFullMode ? 'createTemplate' : 'templates';// for library mode templates page is initial mode and for full mode createTemplates
1542
1428
  if (mode === 'create' && isFullMode) {
1543
1429
  creativesMode = 'createTemplate';
@@ -1625,110 +1511,24 @@ export class Creatives extends React.Component {
1625
1511
  getFormData = (template) => {
1626
1512
  // Always reset isGetFormData so the child does not re-send form data on every re-render
1627
1513
  // (e.g. when user fixes validation error by typing, we must not auto-close the slidebox)
1628
- this.setState(
1629
- (prevState) => {
1630
- const next = { isGetFormData: false };
1631
- if (!template.validity) {
1632
- return next;
1633
- }
1634
- const baseTd = prevState.templateData || template;
1635
- const channel = (
1636
- baseTd?.type
1637
- || template?.type
1638
- || get(template, 'value.type')
1639
- || ''
1640
- ).toUpperCase();
1641
- // Library mode: persist last submitted creatives shape so reopening still hydrates the editor
1642
- // (parent may not merge getCreativesData back into templateData).
1643
- if (this.props.isFullMode === false && template.value) {
1644
- if (channel === constants.RCS) {
1645
- const smsFallBackFromPayload = get(
1646
- template.value,
1647
- 'versions.base.content.RCS.smsFallBackContent',
1648
- );
1649
- const rcsCardVarMappedFromPayload = get(
1650
- template.value,
1651
- 'versions.base.content.RCS.rcsContent.cardContent[0].cardVarMapped',
1652
- );
1653
- next.templateData = {
1654
- ...baseTd,
1655
- type: constants.RCS,
1656
- name: template?.value?.name,
1657
- versions: template?.value?.versions,
1658
- ...(smsFallBackFromPayload != null
1659
- && typeof smsFallBackFromPayload === 'object'
1660
- && Object.keys(smsFallBackFromPayload).length > 0
1661
- ? { smsFallBackContent: smsFallBackFromPayload }
1662
- : {}),
1663
- ...(rcsCardVarMappedFromPayload != null
1664
- && typeof rcsCardVarMappedFromPayload === 'object'
1665
- ? { rcsCardVarMapped: rcsCardVarMappedFromPayload }
1666
- : {}),
1667
- };
1668
- if (template._id) {
1669
- next.templateData._id = template._id;
1670
- }
1671
- } else if (channel === constants.SMS) {
1672
- const submittedSmsTemplateValue = template?.value;
1673
- const smsVersions =
1674
- submittedSmsTemplateValue?.history != null
1675
- ? submittedSmsTemplateValue
1676
- : {
1677
- base: submittedSmsTemplateValue?.base,
1678
- history: submittedSmsTemplateValue?.base
1679
- ? [submittedSmsTemplateValue.base]
1680
- : [],
1681
- };
1682
- next.templateData = {
1683
- ...baseTd,
1684
- type: constants.SMS,
1685
- name: baseTd?.name || 'Campaign message SMS content',
1686
- versions: smsVersions,
1687
- };
1688
- if (template?._id) {
1689
- next.templateData._id = template._id;
1690
- }
1691
- }
1692
- }
1693
- return next;
1694
- },
1695
- () => {
1696
- if (!template.validity) {
1697
- return;
1698
- }
1699
- const templateData = this.state.templateData ? this.state.templateData : template; //select existing or create new content
1700
- const channelForConsumer = String(
1701
- templateData.type
1702
- || template.type
1703
- || get(template, 'value.type')
1704
- || '',
1705
- ).toUpperCase();
1706
- const creativesData = this.getCreativesData(
1707
- channelForConsumer,
1708
- template,
1709
- this.state.templateData || template,
1710
- );// convers data to consumer understandable format
1711
- creativesData.then((data) => {
1712
- this.logGTMEvent(channelForConsumer, data);
1713
- this.processCentralCommsMetaId(channelForConsumer, data, {
1714
- closeSlideBoxAfterSubmit: template.closeSlideBoxAfterSubmit,
1514
+ this.setState({ isGetFormData: false });
1515
+ if (template.validity) {
1516
+ this.setState(
1517
+ {},
1518
+ () => {
1519
+ const templateData = this.state.templateData ? this.state.templateData : template; //select existing or create new content
1520
+ const channel = templateData.type;
1521
+ const creativesData = this.getCreativesData(channel, template, templateData);// convers data to consumer understandable format
1522
+ creativesData.then((data) => {
1523
+ this.logGTMEvent(channel, data);
1524
+ this.processCentralCommsMetaId(channel, data);
1715
1525
  });
1716
- });
1717
- },
1718
- );
1526
+ },
1527
+ );
1528
+ }
1719
1529
  }
1720
1530
 
1721
- processCentralCommsMetaId = (channel, creativesData, options = {}) => {
1722
- const { closeSlideBoxAfterSubmit = false } = options;
1723
- const maybeCloseLibrarySlideBox = () => {
1724
- if (
1725
- closeSlideBoxAfterSubmit
1726
- && this.props.isFullMode === false
1727
- && typeof this.handleCloseSlideBox === 'function'
1728
- ) {
1729
- this.handleCloseSlideBox();
1730
- }
1731
- };
1531
+ processCentralCommsMetaId = (channel, creativesData) => {
1732
1532
  // Create the payload for the centralcommnsmetaId API call
1733
1533
  const { isLoyaltyModule = false, loyaltyMetaData = {} } = this.props;
1734
1534
  const { actionName, setMetaData = () => { } } = loyaltyMetaData;
@@ -1754,7 +1554,6 @@ export class Creatives extends React.Component {
1754
1554
  if (result?.status?.code === 200) {
1755
1555
  setMetaData(result);
1756
1556
  this.props.getCreativesData(creativesData);
1757
- maybeCloseLibrarySlideBox();
1758
1557
  } else {
1759
1558
  CapNotification.error({ message: <FormattedMessage {...messages.somethingWentWrong} /> });
1760
1559
  }
@@ -1765,7 +1564,6 @@ export class Creatives extends React.Component {
1765
1564
  } else {
1766
1565
  // If not a loyalty module or different action, should work as usual
1767
1566
  this.props.getCreativesData(creativesData);
1768
- maybeCloseLibrarySlideBox();
1769
1567
  }
1770
1568
  };
1771
1569
 
@@ -1798,9 +1596,7 @@ export class Creatives extends React.Component {
1798
1596
  }
1799
1597
  this.setState((prevState) => ({
1800
1598
  ...prevState,
1801
- // Library mode (isFullMode === false): retain last template so reopening still has RCS payload.
1802
- // Undefined isFullMode defaults to full-mode close behavior (clear templateData).
1803
- ...(this.props.isFullMode !== false ? { templateData: undefined } : {}),
1599
+ templateData: undefined,
1804
1600
  showSlideBox: false,
1805
1601
  liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
1806
1602
  isLiquidValidationError: false,
@@ -2011,17 +1807,21 @@ export class Creatives extends React.Component {
2011
1807
  }
2012
1808
 
2013
1809
  showLiquidErrorInFooter = (errorMessagesFromFormBuilder, currentFormBuilderTab) => {
2014
- const next = computeLiquidFooterUpdateFromFormBuilder(
2015
- errorMessagesFromFormBuilder,
2016
- this.state.liquidErrorMessage,
2017
- currentFormBuilderTab,
2018
- {
2019
- previousIsLiquidValidationError: this.state.isLiquidValidationError,
2020
- currentChannelUpper: this.state.currentChannel?.toUpperCase(),
2021
- },
2022
- );
2023
- if (next == null) return;
2024
- this.setState(next);
1810
+ const liquidMsgs = get(errorMessagesFromFormBuilder, constants.LIQUID_ERROR_MSG, []);
1811
+ const standardMsgs = get(errorMessagesFromFormBuilder, constants.STANDARD_ERROR_MSG, []);
1812
+ const hasLiquid = !isDeepEmpty(liquidMsgs);
1813
+ const hasStandard = !isDeepEmpty(standardMsgs);
1814
+ const isLiquidValidationError = hasLiquid || hasStandard;
1815
+ // Don't overwrite existing liquid error with empty only for Mobile Push OLD (FormBuilder/clear calls empty there); SMS/others clear on input change
1816
+ const isMobilePush = this.state.currentChannel?.toUpperCase() === constants.MOBILE_PUSH;
1817
+ if (!hasLiquid && !hasStandard && this.state.isLiquidValidationError && isMobilePush) {
1818
+ return;
1819
+ }
1820
+ this.setState({
1821
+ isLiquidValidationError,
1822
+ liquidErrorMessage: errorMessagesFromFormBuilder,
1823
+ activeFormBuilderTab: currentFormBuilderTab === 1 ? constants.ANDROID : (currentFormBuilderTab === 2 ? constants.IOS : null), // Update activeFormBuilderTab, default to 1 if undefined
1824
+ });
2025
1825
  }
2026
1826
 
2027
1827
  // Callback to update HTML Editor validation state (called from EmailWrapper)
@@ -2144,11 +1944,6 @@ export class Creatives extends React.Component {
2144
1944
  inAppEditorType,
2145
1945
  htmlEditorValidationState,
2146
1946
  } = this.state;
2147
- const useLocalTemplates = get(
2148
- this.props,
2149
- 'localTemplatesConfig.useLocalTemplates',
2150
- get(this.props, 'useLocalTemplates', false),
2151
- );
2152
1947
  const {
2153
1948
  isFullMode,
2154
1949
  creativesMode,
@@ -2167,6 +1962,7 @@ export class Creatives extends React.Component {
2167
1962
  smsRegister,
2168
1963
  enableNewChannels,
2169
1964
  eventContextTags,
1965
+ waitEventContextTags = {},
2170
1966
  isLoyaltyModule,
2171
1967
  loyaltyMetaData = {},
2172
1968
  } = this.props;
@@ -2200,7 +1996,14 @@ export class Creatives extends React.Component {
2200
1996
  // IMPORTANT: Never show ErrorInfoNote in footer when in HTML Editor mode, even if liquidErrorMessage exists
2201
1997
  const shouldShowErrorInfoNoteInFooter = isHTMLEditorMode ? false : hasBEEEditorErrors;
2202
1998
 
2203
- const slideBoxWrapperMargin = getSlideBoxWrapperMarginFromLiquidErrors(liquidErrorMessage);
1999
+ // Calculate margin for header/content (always apply if there are errors, regardless of editor type)
2000
+ const slideBoxWrapperMargin = (get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0 && get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0)
2001
+ ? CAP_SPACE_64
2002
+ : get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0
2003
+ ? CAP_SPACE_56
2004
+ : get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0
2005
+ ? CAP_SPACE_32
2006
+ : 0;
2204
2007
  /* TODO: Instead of passing down same props separately to each component down, write common function to these props and pass it accordingly */
2205
2008
 
2206
2009
  // Compute anonymous user type and channel restrictions
@@ -2229,10 +2032,7 @@ export class Creatives extends React.Component {
2229
2032
  <SlideBoxWrapper
2230
2033
  slideBoxWrapperMargin={slideBoxWrapperMargin}
2231
2034
  shouldApplyFooterMargin={shouldShowErrorInfoNoteInFooter}
2232
- className={classnames(
2233
- `${classPrefix} ${isFullMode ? 'creatives-full-mode' : 'creatives-library-mode'} ${mapTemplateCreate ? 'map-template-create' : ''}`,
2234
- useLocalTemplates && slidBoxContent === 'templates' && 'creatives-slidebox--local-sms-templates',
2235
- )}
2035
+ className={classnames(`${classPrefix} ${isFullMode ? 'creatives-full-mode' : 'creatives-library-mode'} ${mapTemplateCreate ? 'map-template-create' : ''}`)}
2236
2036
  >
2237
2037
  <CapSlideBox
2238
2038
  header={
@@ -2257,13 +2057,12 @@ export class Creatives extends React.Component {
2257
2057
  smsRegister={smsRegister}
2258
2058
  handleClose={this.handleCloseSlideBox}
2259
2059
  moduleType={this.props.messageDetails?.type}
2260
- useLocalTemplates={useLocalTemplates}
2261
2060
  />
2262
2061
  )}
2263
2062
  content={(
2264
2063
  <SlideBoxContent
2265
2064
  key="creatives-container-slidebox-content"
2266
- onSelectTemplate={this.props.onSelectTemplate != null ? this.props.onSelectTemplate : this.onSelectTemplate}
2065
+ onSelectTemplate={this.onSelectTemplate}
2267
2066
  onCreateComplete={getCreativesData}
2268
2067
  onPreviewTemplate={this.onPreviewTemplate}
2269
2068
  slidBoxContent={slidBoxContent}
@@ -2331,6 +2130,7 @@ export class Creatives extends React.Component {
2331
2130
  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.
2332
2131
  hostName={this.props?.hostName || ''}
2333
2132
  eventContextTags={eventContextTags}
2133
+ waitEventContextTags={waitEventContextTags}
2334
2134
  isLoyaltyModule={isLoyaltyModule}
2335
2135
  loyaltyMetaData={loyaltyMetaData}
2336
2136
  showTestAndPreviewSlidebox={showTestAndPreviewSlidebox}
@@ -2339,8 +2139,7 @@ export class Creatives extends React.Component {
2339
2139
  isTestAndPreviewMode={(() => this.state.isTestAndPreviewMode)()}
2340
2140
  onHtmlEditorValidationStateChange={this.updateHtmlEditorValidationState}
2341
2141
  onPersonalizationTokensChange={this.handlePersonalizationTokensChange}
2342
- localTemplatesConfig={pick(this.props.localTemplatesConfig || this.props, constants.LOCAL_TEMPLATE_CONFIG_KEYS)}
2343
- />
2142
+ />
2344
2143
  )}
2345
2144
  footer={this.shouldShowFooter() ? (
2346
2145
  <SlideBoxFooter
@@ -2425,31 +2224,13 @@ Creatives.propTypes = {
2425
2224
  orgUnitId: PropTypes.number,
2426
2225
  hostName: PropTypes.string,
2427
2226
  eventContextTags: PropTypes.array,
2227
+ waitEventContextTags: PropTypes.object,
2428
2228
  loyaltyTagFetchingDependencies: PropTypes.object,
2429
2229
  customerType: PropTypes.string,
2430
2230
  intl: PropTypes.shape({
2431
2231
  formatMessage: PropTypes.func,
2432
2232
  }),
2433
2233
  stopValidation: PropTypes.func,
2434
- // Local template list (e.g. for SMS fallback): when set, TemplatesV2 uses these instead of Redux.
2435
- // All optional. Pass either localTemplatesConfig (object) or individual props below.
2436
- localTemplatesConfig: PropTypes.shape({
2437
- useLocalTemplates: PropTypes.bool,
2438
- localTemplates: PropTypes.arrayOf(PropTypes.object),
2439
- localTemplatesLoading: PropTypes.bool,
2440
- localTemplatesFilterContent: PropTypes.node,
2441
- localTemplatesSentinelContent: PropTypes.node,
2442
- localTemplatesScrollContainerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
2443
- localTemplatesUseSkeleton: PropTypes.bool,
2444
- }),
2445
- useLocalTemplates: PropTypes.bool,
2446
- localTemplates: PropTypes.arrayOf(PropTypes.object),
2447
- localTemplatesLoading: PropTypes.bool,
2448
- localTemplatesFilterContent: PropTypes.node,
2449
- localTemplatesSentinelContent: PropTypes.node,
2450
- localTemplatesScrollContainerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
2451
- localTemplatesUseSkeleton: PropTypes.bool,
2452
- onSelectTemplate: PropTypes.func,
2453
2234
  };
2454
2235
  const mapStatesToProps = () => createStructuredSelector({
2455
2236
  isLoading: isLoadingSelector(),