@capillarytech/creatives-library 8.0.345-alpha.12 → 8.0.345-alpha.13

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 (138) hide show
  1. package/constants/unified.js +0 -29
  2. package/package.json +1 -1
  3. package/services/api.js +20 -0
  4. package/services/tests/api.test.js +59 -13
  5. package/utils/commonUtils.js +1 -19
  6. package/v2Components/CapActionButton/constants.js +0 -7
  7. package/v2Components/CapActionButton/index.js +109 -167
  8. package/v2Components/CapActionButton/index.scss +6 -157
  9. package/v2Components/CapActionButton/messages.js +3 -19
  10. package/v2Components/CapActionButton/tests/index.test.js +17 -41
  11. package/v2Components/CapCustomSkeleton/index.js +1 -1
  12. package/v2Components/CapCustomSkeleton/tests/__snapshots__/index.test.js.snap +12 -12
  13. package/v2Components/CapTagList/index.js +0 -10
  14. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +49 -70
  15. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
  16. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -207
  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 -160
  23. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +76 -341
  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 -676
  28. package/v2Components/CommonTestAndPreview/messages.js +3 -49
  29. package/v2Components/CommonTestAndPreview/sagas.js +6 -15
  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 +2 -2
  40. package/v2Components/FormBuilder/index.js +10 -8
  41. package/v2Components/TemplatePreview/_templatePreview.scss +23 -33
  42. package/v2Components/TemplatePreview/index.js +28 -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/Assets/images/archive_Empty_Illustration.svg +9 -0
  48. package/v2Containers/CreativesContainer/SlideBoxContent.js +4 -36
  49. package/v2Containers/CreativesContainer/SlideBoxFooter.js +4 -11
  50. package/v2Containers/CreativesContainer/SlideBoxHeader.js +4 -29
  51. package/v2Containers/CreativesContainer/constants.js +0 -9
  52. package/v2Containers/CreativesContainer/index.js +108 -300
  53. package/v2Containers/CreativesContainer/index.scss +1 -51
  54. package/v2Containers/CreativesContainer/messages.js +4 -0
  55. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -78
  56. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
  57. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
  58. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +98 -357
  59. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +18 -20
  60. package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
  61. package/v2Containers/Rcs/constants.js +8 -119
  62. package/v2Containers/Rcs/index.js +812 -2375
  63. package/v2Containers/Rcs/index.scss +6 -276
  64. package/v2Containers/Rcs/messages.js +3 -38
  65. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +70345 -98302
  66. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +5 -0
  67. package/v2Containers/Rcs/tests/index.test.js +121 -152
  68. package/v2Containers/Rcs/tests/mockData.js +0 -38
  69. package/v2Containers/Rcs/tests/utils.test.js +30 -646
  70. package/v2Containers/Rcs/utils.js +11 -478
  71. package/v2Containers/Sms/Create/index.js +40 -100
  72. package/v2Containers/SmsTrai/Create/index.js +4 -9
  73. package/v2Containers/SmsTrai/Edit/constants.js +0 -2
  74. package/v2Containers/SmsTrai/Edit/index.js +130 -636
  75. package/v2Containers/SmsTrai/Edit/messages.js +4 -14
  76. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2296 -4249
  77. package/v2Containers/SmsWrapper/index.js +8 -37
  78. package/v2Containers/TagList/index.js +0 -6
  79. package/v2Containers/Templates/ChannelTypeIllustration.js +23 -6
  80. package/v2Containers/Templates/_templates.scss +126 -181
  81. package/v2Containers/Templates/actions.js +36 -11
  82. package/v2Containers/Templates/constants.js +23 -2
  83. package/v2Containers/Templates/index.js +333 -142
  84. package/v2Containers/Templates/messages.js +68 -0
  85. package/v2Containers/Templates/reducer.js +68 -0
  86. package/v2Containers/Templates/sagas.js +98 -55
  87. package/v2Containers/Templates/selectors.js +12 -0
  88. package/v2Containers/Templates/tests/ChannelTypeIllustration.test.js +12 -0
  89. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1256 -1042
  90. package/v2Containers/Templates/tests/index.test.js +6 -0
  91. package/v2Containers/Templates/tests/reducer.test.js +178 -0
  92. package/v2Containers/Templates/tests/sagas.test.js +436 -200
  93. package/v2Containers/Templates/tests/selector.test.js +32 -0
  94. package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -72
  95. package/v2Containers/TemplatesV2/index.js +23 -86
  96. package/v2Containers/Whatsapp/index.js +20 -3
  97. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -578
  98. package/utils/rcsPayloadUtils.js +0 -92
  99. package/utils/templateVarUtils.js +0 -201
  100. package/utils/tests/templateVarUtils.test.js +0 -204
  101. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js.rej +0 -18
  102. package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
  103. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
  104. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -87
  105. package/v2Components/SmsFallback/constants.js +0 -73
  106. package/v2Components/SmsFallback/index.js +0 -955
  107. package/v2Components/SmsFallback/index.scss +0 -265
  108. package/v2Components/SmsFallback/messages.js +0 -78
  109. package/v2Components/SmsFallback/smsFallbackUtils.js +0 -118
  110. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
  111. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
  112. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
  113. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -197
  114. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -277
  115. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
  116. package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
  117. package/v2Components/TemplatePreview/constants.js +0 -2
  118. package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
  119. package/v2Components/VarSegmentMessageEditor/index.js +0 -125
  120. package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
  121. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -43
  122. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -67
  123. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
  124. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
  125. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
  126. package/v2Containers/Rcs/index.js.rej +0 -1336
  127. package/v2Containers/Rcs/index.scss.rej +0 -74
  128. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -225
  129. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap.rej +0 -128
  130. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -318
  131. package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
  132. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
  133. package/v2Containers/SmsTrai/Edit/index.scss +0 -121
  134. package/v2Containers/Templates/TemplatesActionBar.js +0 -101
  135. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
  136. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
  137. package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
  138. 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();
@@ -243,6 +260,10 @@ export class Creatives extends React.Component {
243
260
  };
244
261
 
245
262
  onEditTemplate = () => {
263
+ if (this.props.templateData && this.props.templateData.isArchived) {
264
+ CapNotification.error({ message: this.props.intl.formatMessage(messages.cannotEditArchivedTemplate) });
265
+ return;
266
+ }
246
267
  this.setState({ slidBoxContent: 'editTemplate', showSlideBox: true, templateNameExists: true });
247
268
  };
248
269
 
@@ -745,56 +766,25 @@ export class Creatives extends React.Component {
745
766
  smsFallBackContent = {},
746
767
  creativeName = "",
747
768
  channel = constants.RCS,
748
- rcsCardVarMapped,
769
+ accountId = "",
749
770
  } = 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;
771
+ const cardContent = (rcsContent.cardContent && rcsContent.cardContent[0]) || {};
757
772
  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
773
 
783
774
  creativesTemplateData = {
784
775
  type: channel,
785
776
  edit: true,
786
777
  name: creativeName,
787
- ...(includeRootRcsCardVarMapped ? { rcsCardVarMapped: mergedCardVarMapped } : {}),
788
778
  versions: {
789
779
  base: {
790
780
  content: {
791
781
  RCS: {
792
782
  rcsContent: {
793
783
  ...rcsContent,
784
+ ...(accountId && !isFullMode && { accountId }),
794
785
  cardContent: [
795
786
  {
796
787
  ...cardContent,
797
- ...(mergedCardVarMapped ? { cardVarMapped: mergedCardVarMapped } : {}),
798
788
  Status,
799
789
  },
800
790
  ],
@@ -945,10 +935,7 @@ export class Creatives extends React.Component {
945
935
  return newExpandableDetails;
946
936
  }
947
937
 
948
- getCreativesData = async (channelParam, template, templateRecords) => { //from creatives to consumers
949
- const channel = String(
950
- channelParam || template?.type || get(template, 'value.type') || '',
951
- ).toUpperCase();
938
+ getCreativesData = async (channel, template, templateRecords) => { //from creatives to consumers
952
939
  let templateData = { channel };
953
940
  switch (channel) {
954
941
  case constants.SMS:
@@ -1242,7 +1229,7 @@ export class Creatives extends React.Component {
1242
1229
  };
1243
1230
  }
1244
1231
  break;
1245
- case constants.FACEBOOK: {
1232
+ case constants.FACEBOOK:
1246
1233
  if (template.value) {
1247
1234
  const FacebookAd = template?.value?.versions?.base?.content?.FacebookAd;
1248
1235
  const { type } = FacebookAd[0];
@@ -1286,110 +1273,34 @@ export class Creatives extends React.Component {
1286
1273
  selectedMarketingObjective: template.value.selectedMarketingObjective,
1287
1274
  };
1288
1275
  }
1289
- }
1290
1276
  break;
1291
- case constants.RCS: {
1277
+ case constants.RCS:
1292
1278
  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 };
1279
+ const { name = "", versions = {} } = {
1280
+ } = template.value || {};
1281
+ const smsFallBackContent = get(versions, 'base.content.RCS.smsFallBackContent', {});
1334
1282
  const {
1335
- cardContent: cardContentFromSubmit = [],
1283
+ cardContent = [],
1336
1284
  contentType = "",
1337
1285
  cardType = "",
1338
1286
  cardSettings = {},
1287
+ accountId = "",
1339
1288
  } = 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
1289
  const rcsContent = {
1350
1290
  contentType,
1351
1291
  cardType,
1352
1292
  cardSettings,
1353
1293
  cardContent,
1354
1294
  };
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
1295
  templateData = {
1365
1296
  channel,
1366
1297
  creativeName: name,
1367
1298
  rcsContent,
1368
- ...(includeRootRcsCardVarMappedOnConsumerPayload
1369
- ? { rcsCardVarMapped: cardVarMappedFromFirstRcsCard }
1370
- : {}),
1299
+ accountId,
1371
1300
  };
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
1301
  }
1390
- }
1391
1302
  break;
1392
- case constants.ZALO: {
1303
+ case constants.ZALO:
1393
1304
  if (template.value) {
1394
1305
  templateData = {
1395
1306
  ...template.value,
@@ -1398,7 +1309,6 @@ export class Creatives extends React.Component {
1398
1309
  delete templateData.type;
1399
1310
  }
1400
1311
  }
1401
- }
1402
1312
  break;
1403
1313
  case constants.WEBPUSH: {
1404
1314
  if (template.value) {
@@ -1505,10 +1415,7 @@ export class Creatives extends React.Component {
1505
1415
  return templateData;
1506
1416
  };
1507
1417
 
1508
- getSlideBoxContent({ mode, templateData, isFullMode, useLocalTemplates }) {
1509
- if (useLocalTemplates && mode === 'create' && isEmpty(templateData)) {
1510
- return 'templates';
1511
- }
1418
+ getSlideBoxContent({ mode, templateData, isFullMode }) {
1512
1419
  let creativesMode = isFullMode ? 'createTemplate' : 'templates';// for library mode templates page is initial mode and for full mode createTemplates
1513
1420
  if (mode === 'create' && isFullMode) {
1514
1421
  creativesMode = 'createTemplate';
@@ -1596,110 +1503,24 @@ export class Creatives extends React.Component {
1596
1503
  getFormData = (template) => {
1597
1504
  // Always reset isGetFormData so the child does not re-send form data on every re-render
1598
1505
  // (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,
1506
+ this.setState({ isGetFormData: false });
1507
+ if (template.validity) {
1508
+ this.setState(
1509
+ {},
1510
+ () => {
1511
+ const templateData = this.state.templateData ? this.state.templateData : template; //select existing or create new content
1512
+ const channel = templateData.type;
1513
+ const creativesData = this.getCreativesData(channel, template, templateData);// convers data to consumer understandable format
1514
+ creativesData.then((data) => {
1515
+ this.logGTMEvent(channel, data);
1516
+ this.processCentralCommsMetaId(channel, data);
1686
1517
  });
1687
- });
1688
- },
1689
- );
1518
+ },
1519
+ );
1520
+ }
1690
1521
  }
1691
1522
 
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
- };
1523
+ processCentralCommsMetaId = (channel, creativesData) => {
1703
1524
  // Create the payload for the centralcommnsmetaId API call
1704
1525
  const { isLoyaltyModule = false, loyaltyMetaData = {} } = this.props;
1705
1526
  const { actionName, setMetaData = () => { } } = loyaltyMetaData;
@@ -1725,7 +1546,6 @@ export class Creatives extends React.Component {
1725
1546
  if (result?.status?.code === 200) {
1726
1547
  setMetaData(result);
1727
1548
  this.props.getCreativesData(creativesData);
1728
- maybeCloseLibrarySlideBox();
1729
1549
  } else {
1730
1550
  CapNotification.error({ message: <FormattedMessage {...messages.somethingWentWrong} /> });
1731
1551
  }
@@ -1736,7 +1556,6 @@ export class Creatives extends React.Component {
1736
1556
  } else {
1737
1557
  // If not a loyalty module or different action, should work as usual
1738
1558
  this.props.getCreativesData(creativesData);
1739
- maybeCloseLibrarySlideBox();
1740
1559
  }
1741
1560
  };
1742
1561
 
@@ -1769,9 +1588,7 @@ export class Creatives extends React.Component {
1769
1588
  }
1770
1589
  this.setState((prevState) => ({
1771
1590
  ...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 } : {}),
1591
+ templateData: undefined,
1775
1592
  showSlideBox: false,
1776
1593
  liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
1777
1594
  isLiquidValidationError: false,
@@ -1982,12 +1799,21 @@ export class Creatives extends React.Component {
1982
1799
  }
1983
1800
 
1984
1801
  showLiquidErrorInFooter = (errorMessagesFromFormBuilder, currentFormBuilderTab) => {
1985
- const next = computeLiquidFooterUpdateFromFormBuilder(errorMessagesFromFormBuilder, currentFormBuilderTab, {
1986
- previousIsLiquidValidationError: this.state.isLiquidValidationError,
1987
- currentChannelUpper: this.state.currentChannel?.toUpperCase(),
1802
+ const liquidMsgs = get(errorMessagesFromFormBuilder, constants.LIQUID_ERROR_MSG, []);
1803
+ const standardMsgs = get(errorMessagesFromFormBuilder, constants.STANDARD_ERROR_MSG, []);
1804
+ const hasLiquid = !isDeepEmpty(liquidMsgs);
1805
+ const hasStandard = !isDeepEmpty(standardMsgs);
1806
+ const isLiquidValidationError = hasLiquid || hasStandard;
1807
+ // Don't overwrite existing liquid error with empty only for Mobile Push OLD (FormBuilder/clear calls empty there); SMS/others clear on input change
1808
+ const isMobilePush = this.state.currentChannel?.toUpperCase() === constants.MOBILE_PUSH;
1809
+ if (!hasLiquid && !hasStandard && this.state.isLiquidValidationError && isMobilePush) {
1810
+ return;
1811
+ }
1812
+ this.setState({
1813
+ isLiquidValidationError,
1814
+ liquidErrorMessage: errorMessagesFromFormBuilder,
1815
+ activeFormBuilderTab: currentFormBuilderTab === 1 ? constants.ANDROID : (currentFormBuilderTab === 2 ? constants.IOS : null), // Update activeFormBuilderTab, default to 1 if undefined
1988
1816
  });
1989
- if (next == null) return;
1990
- this.setState(next);
1991
1817
  }
1992
1818
 
1993
1819
  // Callback to update HTML Editor validation state (called from EmailWrapper)
@@ -2110,11 +1936,6 @@ export class Creatives extends React.Component {
2110
1936
  inAppEditorType,
2111
1937
  htmlEditorValidationState,
2112
1938
  } = this.state;
2113
- const useLocalTemplates = get(
2114
- this.props,
2115
- 'localTemplatesConfig.useLocalTemplates',
2116
- get(this.props, 'useLocalTemplates', false),
2117
- );
2118
1939
  const {
2119
1940
  isFullMode,
2120
1941
  creativesMode,
@@ -2133,6 +1954,7 @@ export class Creatives extends React.Component {
2133
1954
  smsRegister,
2134
1955
  enableNewChannels,
2135
1956
  eventContextTags,
1957
+ waitEventContextTags = {},
2136
1958
  isLoyaltyModule,
2137
1959
  loyaltyMetaData = {},
2138
1960
  } = this.props;
@@ -2166,7 +1988,14 @@ export class Creatives extends React.Component {
2166
1988
  // IMPORTANT: Never show ErrorInfoNote in footer when in HTML Editor mode, even if liquidErrorMessage exists
2167
1989
  const shouldShowErrorInfoNoteInFooter = isHTMLEditorMode ? false : hasBEEEditorErrors;
2168
1990
 
2169
- const slideBoxWrapperMargin = getSlideBoxWrapperMarginFromLiquidErrors(liquidErrorMessage);
1991
+ // Calculate margin for header/content (always apply if there are errors, regardless of editor type)
1992
+ const slideBoxWrapperMargin = (get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0 && get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0)
1993
+ ? CAP_SPACE_64
1994
+ : get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0
1995
+ ? CAP_SPACE_56
1996
+ : get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0
1997
+ ? CAP_SPACE_32
1998
+ : 0;
2170
1999
  /* TODO: Instead of passing down same props separately to each component down, write common function to these props and pass it accordingly */
2171
2000
 
2172
2001
  // Compute anonymous user type and channel restrictions
@@ -2195,10 +2024,7 @@ export class Creatives extends React.Component {
2195
2024
  <SlideBoxWrapper
2196
2025
  slideBoxWrapperMargin={slideBoxWrapperMargin}
2197
2026
  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
- )}
2027
+ className={classnames(`${classPrefix} ${isFullMode ? 'creatives-full-mode' : 'creatives-library-mode'} ${mapTemplateCreate ? 'map-template-create' : ''}`)}
2202
2028
  >
2203
2029
  <CapSlideBox
2204
2030
  header={
@@ -2223,13 +2049,12 @@ export class Creatives extends React.Component {
2223
2049
  smsRegister={smsRegister}
2224
2050
  handleClose={this.handleCloseSlideBox}
2225
2051
  moduleType={this.props.messageDetails?.type}
2226
- useLocalTemplates={useLocalTemplates}
2227
2052
  />
2228
2053
  )}
2229
2054
  content={(
2230
2055
  <SlideBoxContent
2231
2056
  key="creatives-container-slidebox-content"
2232
- onSelectTemplate={this.props.onSelectTemplate != null ? this.props.onSelectTemplate : this.onSelectTemplate}
2057
+ onSelectTemplate={this.onSelectTemplate}
2233
2058
  onCreateComplete={getCreativesData}
2234
2059
  onPreviewTemplate={this.onPreviewTemplate}
2235
2060
  slidBoxContent={slidBoxContent}
@@ -2297,6 +2122,7 @@ export class Creatives extends React.Component {
2297
2122
  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
2123
  hostName={this.props?.hostName || ''}
2299
2124
  eventContextTags={eventContextTags}
2125
+ waitEventContextTags={waitEventContextTags}
2300
2126
  isLoyaltyModule={isLoyaltyModule}
2301
2127
  loyaltyMetaData={loyaltyMetaData}
2302
2128
  showTestAndPreviewSlidebox={showTestAndPreviewSlidebox}
@@ -2305,8 +2131,7 @@ export class Creatives extends React.Component {
2305
2131
  isTestAndPreviewMode={(() => this.state.isTestAndPreviewMode)()}
2306
2132
  onHtmlEditorValidationStateChange={this.updateHtmlEditorValidationState}
2307
2133
  onPersonalizationTokensChange={this.handlePersonalizationTokensChange}
2308
- localTemplatesConfig={pick(this.props.localTemplatesConfig || this.props, constants.LOCAL_TEMPLATE_CONFIG_KEYS)}
2309
- />
2134
+ />
2310
2135
  )}
2311
2136
  footer={this.shouldShowFooter() ? (
2312
2137
  <SlideBoxFooter
@@ -2315,6 +2140,7 @@ export class Creatives extends React.Component {
2315
2140
  onSave={this.saveMessage}
2316
2141
  onDiscard={this.discardMessage}
2317
2142
  onEditTemplate={this.onEditTemplate}
2143
+ isTemplateArchived={!!(this.props.templateData && this.props.templateData.isArchived)}
2318
2144
  slidBoxContent={slidBoxContent}
2319
2145
  onCreateNextStep={this.onCreateNextStep}
2320
2146
  currentChannel={currentChannel.toUpperCase()}
@@ -2390,31 +2216,13 @@ Creatives.propTypes = {
2390
2216
  orgUnitId: PropTypes.number,
2391
2217
  hostName: PropTypes.string,
2392
2218
  eventContextTags: PropTypes.array,
2219
+ waitEventContextTags: PropTypes.object,
2393
2220
  loyaltyTagFetchingDependencies: PropTypes.object,
2394
2221
  customerType: PropTypes.string,
2395
2222
  intl: PropTypes.shape({
2396
2223
  formatMessage: PropTypes.func,
2397
2224
  }),
2398
2225
  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
2226
  };
2419
2227
  const mapStatesToProps = () => createStructuredSelector({
2420
2228
  isLoading: isLoadingSelector(),