@capillarytech/creatives-library 8.0.330-alpha.0 → 8.0.331

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