@capillarytech/creatives-library 8.0.329 → 8.0.330

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 -14
  2. package/package.json +1 -1
  3. package/services/api.js +0 -17
  4. package/services/tests/api.test.js +0 -85
  5. package/utils/commonUtils.js +0 -10
  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 -1006
  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 -8
  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 +93 -286
  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 +1 -34
  53. package/v2Containers/Rcs/index.js +884 -999
  54. package/v2Containers/Rcs/index.scss +6 -85
  55. package/v2Containers/Rcs/messages.js +1 -10
  56. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +2453 -41456
  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 -379
  61. package/v2Containers/Rcs/utils.js +10 -358
  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 -609
  66. package/v2Containers/SmsTrai/Edit/messages.js +4 -9
  67. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2600 -4586
  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 -172
  82. package/utils/tests/templateVarUtils.test.js +0 -160
  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 -107
  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 -261
  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 -205
  114. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -251
  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,8 +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 { RCS_SMS_FALLBACK_VAR_MAPPED_PROP } from '../../v2Components/CommonTestAndPreview/constants';
47
50
  import { CREATIVE } from '../Facebook/constants';
48
51
  import { LOYALTY } from '../App/constants';
49
52
  import {
@@ -58,11 +61,6 @@ import { capSagaForFetchSchemaForEntity, capSagaLiquidEntity } from '../Cap/saga
58
61
  import { v2TemplateSagaWatchGetDefaultBeeTemplates } from '../Templates/sagas';
59
62
  import { DYNAMIC_URL } from '../../v2Components/CapWhatsappCTA/constants';
60
63
  import ErrorInfoNote from '../../v2Components/ErrorInfoNote';
61
- import SlideBoxWrapper from './CreativesSlideBoxWrapper';
62
- import {
63
- computeLiquidFooterUpdateFromFormBuilder,
64
- getSlideBoxWrapperMarginFromLiquidErrors,
65
- } from './embeddedSlideboxUtils';
66
64
 
67
65
  import {
68
66
  transformChannelPayload,
@@ -71,24 +69,51 @@ import {
71
69
  import { MANUAL_CAROUSEL } from '../MobilePushNew/constants';
72
70
  import { BIG_HTML } from '../InApp/constants';
73
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
+
74
89
  const classPrefix = 'add-creatives-section';
75
90
  const CREATIVES_CONTAINER = 'creativesContainer';
76
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
+ `;
77
112
  export class Creatives extends React.Component {
78
113
  constructor(props) {
79
114
  super(props);
80
115
 
81
- const useLocalTemplates = get(
82
- props,
83
- 'localTemplatesConfig.useLocalTemplates',
84
- get(props, 'useLocalTemplates', false),
85
- );
86
- const initialSlidBoxContent = this.getSlideBoxContent({
87
- mode: props.creativesMode,
88
- templateData: props.templateData,
89
- isFullMode: props.isFullMode,
90
- useLocalTemplates,
91
- });
116
+ const initialSlidBoxContent = this.getSlideBoxContent({ mode: props.creativesMode, templateData: props.templateData, isFullMode: props.isFullMode });
92
117
 
93
118
  this.state = {
94
119
  isLoadingContent: true,
@@ -135,13 +160,7 @@ export class Creatives extends React.Component {
135
160
  }
136
161
 
137
162
  componentWillUnmount() {
138
- const isEmbedded = get(this.props, 'location.query.type', '') === "embedded";
139
- const useLocalTemplates = get(
140
- this.props,
141
- 'localTemplatesConfig.useLocalTemplates',
142
- get(this.props, 'useLocalTemplates', false),
143
- );
144
- if (isEmbedded && !useLocalTemplates) {
163
+ if (get(this.props, 'location.query.type', '') === "embedded") {
145
164
  this.props.templateActions.resetTemplateStoreData();
146
165
  }
147
166
  this.props.globalActions.clearMetaEntities();
@@ -743,41 +762,14 @@ export class Creatives extends React.Component {
743
762
  smsFallBackContent = {},
744
763
  creativeName = "",
745
764
  channel = constants.RCS,
746
- rcsCardVarMapped,
747
765
  } = templateData || {};
748
- const { isFullMode: isFullModeForRcsPayload } = this.props;
749
- const firstCardIn = (rcsContent.cardContent && rcsContent.cardContent[0]) || {};
750
- const {
751
- cardDisplayTitle: _omitDispTitleIn,
752
- cardDisplayDescription: _omitDispDescIn,
753
- ...cardContent
754
- } = firstCardIn;
766
+ const cardContent = (rcsContent.cardContent && rcsContent.cardContent[0]) || {};
755
767
  const Status = RCS_STATUSES.approved || '';
756
- const mergedCardVarMapped = (() => {
757
- const nestedCardVarMapped = cardContent?.cardVarMapped;
758
- const rootMirrorCardVarMapped = rcsCardVarMapped;
759
- const nestedRecord =
760
- nestedCardVarMapped != null && typeof nestedCardVarMapped === 'object'
761
- ? nestedCardVarMapped
762
- : {};
763
- const rootRecord =
764
- rootMirrorCardVarMapped != null && typeof rootMirrorCardVarMapped === 'object'
765
- ? rootMirrorCardVarMapped
766
- : {};
767
- const mergedFromRootAndNested = { ...rootRecord, ...nestedRecord };
768
- return Object.keys(mergedFromRootAndNested).length > 0 ? mergedFromRootAndNested : null;
769
- })();
770
- // Campaigns (embedded): do not duplicate `cardVarMapped` as root `rcsCardVarMapped` on send —
771
- // slot map stays on `versions…cardContent[0].cardVarMapped` only. Library full mode keeps root mirror.
772
- // Use `=== true` so omitted/undefined `isFullMode` does not behave like library (avoids duplicate on approval payload).
773
- const includeRootRcsCardVarMapped =
774
- mergedCardVarMapped && isFullModeForRcsPayload === true;
775
768
 
776
769
  creativesTemplateData = {
777
770
  type: channel,
778
771
  edit: true,
779
772
  name: creativeName,
780
- ...(includeRootRcsCardVarMapped ? { rcsCardVarMapped: mergedCardVarMapped } : {}),
781
773
  versions: {
782
774
  base: {
783
775
  content: {
@@ -787,7 +779,6 @@ export class Creatives extends React.Component {
787
779
  cardContent: [
788
780
  {
789
781
  ...cardContent,
790
- ...(mergedCardVarMapped ? { cardVarMapped: mergedCardVarMapped } : {}),
791
782
  Status,
792
783
  },
793
784
  ],
@@ -938,10 +929,7 @@ export class Creatives extends React.Component {
938
929
  return newExpandableDetails;
939
930
  }
940
931
 
941
- getCreativesData = async (channelParam, template, templateRecords) => { //from creatives to consumers
942
- const channel = String(
943
- channelParam || template?.type || get(template, 'value.type') || '',
944
- ).toUpperCase();
932
+ getCreativesData = async (channel, template, templateRecords) => { //from creatives to consumers
945
933
  let templateData = { channel };
946
934
  switch (channel) {
947
935
  case constants.SMS:
@@ -1283,101 +1271,26 @@ export class Creatives extends React.Component {
1283
1271
  break;
1284
1272
  case constants.RCS: {
1285
1273
  if (template.value) {
1286
- const { isFullMode: isFullModeForRcsConsumerPayload } = this.props;
1287
- const { name = "", versions = {} } = template.value || {};
1288
- const fromSubmit = get(versions, 'base.content.RCS.smsFallBackContent', {});
1289
- const fromRecords = {
1290
- ...(templateRecords?.smsFallBackContent || {}),
1291
- ...(get(templateRecords, 'versions.base.content.RCS.smsFallBackContent') || {}),
1292
- };
1293
- const hasMeaningfulRcsSmsFallback = (smsFallbackPayload) => {
1294
- if (
1295
- !smsFallbackPayload
1296
- || typeof smsFallbackPayload !== 'object'
1297
- || Object.keys(smsFallbackPayload).length === 0
1298
- ) {
1299
- return false;
1300
- }
1301
- const fallbackBodyText = String(
1302
- smsFallbackPayload.smsContent
1303
- ?? smsFallbackPayload.smsTemplateContent
1304
- ?? smsFallbackPayload.message
1305
- ?? smsFallbackPayload.content
1306
- ?? '',
1307
- ).trim();
1308
- const fallbackTemplateName = String(
1309
- smsFallbackPayload.smsTemplateName ?? smsFallbackPayload.templateName ?? '',
1310
- ).trim();
1311
- const rcsSmsFallbackVarMapped =
1312
- smsFallbackPayload?.[RCS_SMS_FALLBACK_VAR_MAPPED_PROP];
1313
- const hasVarMappedEntries =
1314
- rcsSmsFallbackVarMapped != null
1315
- && typeof rcsSmsFallbackVarMapped === 'object'
1316
- && Object.keys(rcsSmsFallbackVarMapped).length > 0;
1317
- return (
1318
- fallbackBodyText !== ''
1319
- || fallbackTemplateName !== ''
1320
- || hasVarMappedEntries
1321
- );
1322
- };
1323
- // If submit has only empty strings, do not let it wipe fallback mirrored on templateRecords (library round-trip).
1324
- const smsFallBackContent = hasMeaningfulRcsSmsFallback(fromSubmit)
1325
- ? { ...fromRecords, ...fromSubmit }
1326
- : { ...fromSubmit, ...fromRecords };
1274
+ const { name = "", versions = {} } = {
1275
+ } = template.value || {};
1276
+ const smsFallBackContent = get(versions, 'base.content.RCS.smsFallBackContent', {});
1327
1277
  const {
1328
- cardContent: cardContentFromSubmit = [],
1278
+ cardContent = [],
1329
1279
  contentType = "",
1330
1280
  cardType = "",
1331
1281
  cardSettings = {},
1332
1282
  } = get(versions, 'base.content.RCS.rcsContent', {});
1333
- const rootRcsCardVarMappedFromSubmit = get(template, 'value.rcsCardVarMapped');
1334
- const firstCardFromSubmit = Array.isArray(cardContentFromSubmit)
1335
- ? cardContentFromSubmit[0]
1336
- : null;
1337
- const cardContent = mapRcsCardContentForConsumerWithResolvedTags(
1338
- cardContentFromSubmit,
1339
- rootRcsCardVarMappedFromSubmit,
1340
- isFullModeForRcsConsumerPayload,
1341
- );
1342
1283
  const rcsContent = {
1343
1284
  contentType,
1344
1285
  cardType,
1345
1286
  cardSettings,
1346
1287
  cardContent,
1347
1288
  };
1348
- const cardVarMappedFromFirstRcsCard =
1349
- firstCardFromSubmit?.cardVarMapped != null
1350
- && typeof firstCardFromSubmit.cardVarMapped === 'object'
1351
- ? firstCardFromSubmit.cardVarMapped
1352
- : null;
1353
- const includeRootRcsCardVarMappedOnConsumerPayload =
1354
- cardVarMappedFromFirstRcsCard
1355
- && Object.keys(cardVarMappedFromFirstRcsCard).length > 0
1356
- && isFullModeForRcsConsumerPayload === true;
1357
1289
  templateData = {
1358
1290
  channel,
1359
1291
  creativeName: name,
1360
1292
  rcsContent,
1361
- ...(includeRootRcsCardVarMappedOnConsumerPayload
1362
- ? { rcsCardVarMapped: cardVarMappedFromFirstRcsCard }
1363
- : {}),
1364
1293
  };
1365
- // Library / campaign consumers round-trip templateData via getTemplateData; include SMS fallback
1366
- // so reopening the editor restores fallback text and tag mappings.
1367
- // cap-campaigns-v2 API expects `smsFallBackContent.message` (see normalizeRcsMessageContentForApi).
1368
- if (hasMeaningfulRcsSmsFallback(smsFallBackContent)) {
1369
- const smsText =
1370
- smsFallBackContent.message
1371
- ?? smsFallBackContent.smsContent
1372
- ?? smsFallBackContent.smsTemplateContent
1373
- ?? '';
1374
- templateData.smsFallBackContent = {
1375
- ...smsFallBackContent,
1376
- ...(String(smsText).trim() !== ''
1377
- ? { message: String(smsText).trim() }
1378
- : {}),
1379
- };
1380
- }
1381
1294
  }
1382
1295
  }
1383
1296
  break;
@@ -1497,10 +1410,7 @@ export class Creatives extends React.Component {
1497
1410
  return templateData;
1498
1411
  };
1499
1412
 
1500
- getSlideBoxContent({ mode, templateData, isFullMode, useLocalTemplates }) {
1501
- if (useLocalTemplates && mode === 'create' && isEmpty(templateData)) {
1502
- return 'templates';
1503
- }
1413
+ getSlideBoxContent({ mode, templateData, isFullMode }) {
1504
1414
  let creativesMode = isFullMode ? 'createTemplate' : 'templates';// for library mode templates page is initial mode and for full mode createTemplates
1505
1415
  if (mode === 'create' && isFullMode) {
1506
1416
  creativesMode = 'createTemplate';
@@ -1588,110 +1498,24 @@ export class Creatives extends React.Component {
1588
1498
  getFormData = (template) => {
1589
1499
  // Always reset isGetFormData so the child does not re-send form data on every re-render
1590
1500
  // (e.g. when user fixes validation error by typing, we must not auto-close the slidebox)
1591
- this.setState(
1592
- (prevState) => {
1593
- const next = { isGetFormData: false };
1594
- if (!template.validity) {
1595
- return next;
1596
- }
1597
- const baseTd = prevState.templateData || template;
1598
- const channel = (
1599
- baseTd?.type
1600
- || template?.type
1601
- || get(template, 'value.type')
1602
- || ''
1603
- ).toUpperCase();
1604
- // Library mode: persist last submitted creatives shape so reopening still hydrates the editor
1605
- // (parent may not merge getCreativesData back into templateData).
1606
- if (this.props.isFullMode === false && template.value) {
1607
- if (channel === constants.RCS) {
1608
- const smsFallBackFromPayload = get(
1609
- template.value,
1610
- 'versions.base.content.RCS.smsFallBackContent',
1611
- );
1612
- const rcsCardVarMappedFromPayload = get(
1613
- template.value,
1614
- 'versions.base.content.RCS.rcsContent.cardContent[0].cardVarMapped',
1615
- );
1616
- next.templateData = {
1617
- ...baseTd,
1618
- type: constants.RCS,
1619
- name: template?.value?.name,
1620
- versions: template?.value?.versions,
1621
- ...(smsFallBackFromPayload != null
1622
- && typeof smsFallBackFromPayload === 'object'
1623
- && Object.keys(smsFallBackFromPayload).length > 0
1624
- ? { smsFallBackContent: smsFallBackFromPayload }
1625
- : {}),
1626
- ...(rcsCardVarMappedFromPayload != null
1627
- && typeof rcsCardVarMappedFromPayload === 'object'
1628
- ? { rcsCardVarMapped: rcsCardVarMappedFromPayload }
1629
- : {}),
1630
- };
1631
- if (template._id) {
1632
- next.templateData._id = template._id;
1633
- }
1634
- } else if (channel === constants.SMS) {
1635
- const submittedSmsTemplateValue = template?.value;
1636
- const smsVersions =
1637
- submittedSmsTemplateValue?.history != null
1638
- ? submittedSmsTemplateValue
1639
- : {
1640
- base: submittedSmsTemplateValue?.base,
1641
- history: submittedSmsTemplateValue?.base
1642
- ? [submittedSmsTemplateValue.base]
1643
- : [],
1644
- };
1645
- next.templateData = {
1646
- ...baseTd,
1647
- type: constants.SMS,
1648
- name: baseTd?.name || 'Campaign message SMS content',
1649
- versions: smsVersions,
1650
- };
1651
- if (template?._id) {
1652
- next.templateData._id = template._id;
1653
- }
1654
- }
1655
- }
1656
- return next;
1657
- },
1658
- () => {
1659
- if (!template.validity) {
1660
- return;
1661
- }
1662
- const templateData = this.state.templateData ? this.state.templateData : template; //select existing or create new content
1663
- const channelForConsumer = String(
1664
- templateData.type
1665
- || template.type
1666
- || get(template, 'value.type')
1667
- || '',
1668
- ).toUpperCase();
1669
- const creativesData = this.getCreativesData(
1670
- channelForConsumer,
1671
- template,
1672
- this.state.templateData || template,
1673
- );// convers data to consumer understandable format
1674
- creativesData.then((data) => {
1675
- this.logGTMEvent(channelForConsumer, data);
1676
- this.processCentralCommsMetaId(channelForConsumer, data, {
1677
- closeSlideBoxAfterSubmit: template.closeSlideBoxAfterSubmit,
1501
+ this.setState({ isGetFormData: false });
1502
+ if (template.validity) {
1503
+ this.setState(
1504
+ {},
1505
+ () => {
1506
+ const templateData = this.state.templateData ? this.state.templateData : template; //select existing or create new content
1507
+ const channel = templateData.type;
1508
+ const creativesData = this.getCreativesData(channel, template, templateData);// convers data to consumer understandable format
1509
+ creativesData.then((data) => {
1510
+ this.logGTMEvent(channel, data);
1511
+ this.processCentralCommsMetaId(channel, data);
1678
1512
  });
1679
- });
1680
- },
1681
- );
1513
+ },
1514
+ );
1515
+ }
1682
1516
  }
1683
1517
 
1684
- processCentralCommsMetaId = (channel, creativesData, options = {}) => {
1685
- const { closeSlideBoxAfterSubmit = false } = options;
1686
- const maybeCloseLibrarySlideBox = () => {
1687
- if (
1688
- closeSlideBoxAfterSubmit
1689
- && this.props.isFullMode === false
1690
- && typeof this.handleCloseSlideBox === 'function'
1691
- ) {
1692
- this.handleCloseSlideBox();
1693
- }
1694
- };
1518
+ processCentralCommsMetaId = (channel, creativesData) => {
1695
1519
  // Create the payload for the centralcommnsmetaId API call
1696
1520
  const { isLoyaltyModule = false, loyaltyMetaData = {} } = this.props;
1697
1521
  const { actionName, setMetaData = () => { } } = loyaltyMetaData;
@@ -1717,7 +1541,6 @@ export class Creatives extends React.Component {
1717
1541
  if (result?.status?.code === 200) {
1718
1542
  setMetaData(result);
1719
1543
  this.props.getCreativesData(creativesData);
1720
- maybeCloseLibrarySlideBox();
1721
1544
  } else {
1722
1545
  CapNotification.error({ message: <FormattedMessage {...messages.somethingWentWrong} /> });
1723
1546
  }
@@ -1728,7 +1551,6 @@ export class Creatives extends React.Component {
1728
1551
  } else {
1729
1552
  // If not a loyalty module or different action, should work as usual
1730
1553
  this.props.getCreativesData(creativesData);
1731
- maybeCloseLibrarySlideBox();
1732
1554
  }
1733
1555
  };
1734
1556
 
@@ -1761,9 +1583,7 @@ export class Creatives extends React.Component {
1761
1583
  }
1762
1584
  this.setState((prevState) => ({
1763
1585
  ...prevState,
1764
- // Library mode (isFullMode === false): retain last template so reopening still has RCS payload.
1765
- // Undefined isFullMode defaults to full-mode close behavior (clear templateData).
1766
- ...(this.props.isFullMode !== false ? { templateData: undefined } : {}),
1586
+ templateData: undefined,
1767
1587
  showSlideBox: false,
1768
1588
  liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
1769
1589
  isLiquidValidationError: false,
@@ -1974,12 +1794,21 @@ export class Creatives extends React.Component {
1974
1794
  }
1975
1795
 
1976
1796
  showLiquidErrorInFooter = (errorMessagesFromFormBuilder, currentFormBuilderTab) => {
1977
- const next = computeLiquidFooterUpdateFromFormBuilder(errorMessagesFromFormBuilder, currentFormBuilderTab, {
1978
- previousIsLiquidValidationError: this.state.isLiquidValidationError,
1979
- currentChannelUpper: this.state.currentChannel?.toUpperCase(),
1797
+ const liquidMsgs = get(errorMessagesFromFormBuilder, constants.LIQUID_ERROR_MSG, []);
1798
+ const standardMsgs = get(errorMessagesFromFormBuilder, constants.STANDARD_ERROR_MSG, []);
1799
+ const hasLiquid = !isDeepEmpty(liquidMsgs);
1800
+ const hasStandard = !isDeepEmpty(standardMsgs);
1801
+ const isLiquidValidationError = hasLiquid || hasStandard;
1802
+ // Don't overwrite existing liquid error with empty only for Mobile Push OLD (FormBuilder/clear calls empty there); SMS/others clear on input change
1803
+ const isMobilePush = this.state.currentChannel?.toUpperCase() === constants.MOBILE_PUSH;
1804
+ if (!hasLiquid && !hasStandard && this.state.isLiquidValidationError && isMobilePush) {
1805
+ return;
1806
+ }
1807
+ this.setState({
1808
+ isLiquidValidationError,
1809
+ liquidErrorMessage: errorMessagesFromFormBuilder,
1810
+ activeFormBuilderTab: currentFormBuilderTab === 1 ? constants.ANDROID : (currentFormBuilderTab === 2 ? constants.IOS : null), // Update activeFormBuilderTab, default to 1 if undefined
1980
1811
  });
1981
- if (next == null) return;
1982
- this.setState(next);
1983
1812
  }
1984
1813
 
1985
1814
  // Callback to update HTML Editor validation state (called from EmailWrapper)
@@ -2102,11 +1931,6 @@ export class Creatives extends React.Component {
2102
1931
  inAppEditorType,
2103
1932
  htmlEditorValidationState,
2104
1933
  } = this.state;
2105
- const useLocalTemplates = get(
2106
- this.props,
2107
- 'localTemplatesConfig.useLocalTemplates',
2108
- get(this.props, 'useLocalTemplates', false),
2109
- );
2110
1934
  const {
2111
1935
  isFullMode,
2112
1936
  creativesMode,
@@ -2158,7 +1982,14 @@ export class Creatives extends React.Component {
2158
1982
  // IMPORTANT: Never show ErrorInfoNote in footer when in HTML Editor mode, even if liquidErrorMessage exists
2159
1983
  const shouldShowErrorInfoNoteInFooter = isHTMLEditorMode ? false : hasBEEEditorErrors;
2160
1984
 
2161
- const slideBoxWrapperMargin = getSlideBoxWrapperMarginFromLiquidErrors(liquidErrorMessage);
1985
+ // Calculate margin for header/content (always apply if there are errors, regardless of editor type)
1986
+ const slideBoxWrapperMargin = (get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0 && get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0)
1987
+ ? CAP_SPACE_64
1988
+ : get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0
1989
+ ? CAP_SPACE_56
1990
+ : get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0
1991
+ ? CAP_SPACE_32
1992
+ : 0;
2162
1993
  /* TODO: Instead of passing down same props separately to each component down, write common function to these props and pass it accordingly */
2163
1994
 
2164
1995
  // Compute anonymous user type and channel restrictions
@@ -2187,10 +2018,7 @@ export class Creatives extends React.Component {
2187
2018
  <SlideBoxWrapper
2188
2019
  slideBoxWrapperMargin={slideBoxWrapperMargin}
2189
2020
  shouldApplyFooterMargin={shouldShowErrorInfoNoteInFooter}
2190
- className={classnames(
2191
- `${classPrefix} ${isFullMode ? 'creatives-full-mode' : 'creatives-library-mode'} ${mapTemplateCreate ? 'map-template-create' : ''}`,
2192
- useLocalTemplates && slidBoxContent === 'templates' && 'creatives-slidebox--local-sms-templates',
2193
- )}
2021
+ className={classnames(`${classPrefix} ${isFullMode ? 'creatives-full-mode' : 'creatives-library-mode'} ${mapTemplateCreate ? 'map-template-create' : ''}`)}
2194
2022
  >
2195
2023
  <CapSlideBox
2196
2024
  header={
@@ -2215,13 +2043,12 @@ export class Creatives extends React.Component {
2215
2043
  smsRegister={smsRegister}
2216
2044
  handleClose={this.handleCloseSlideBox}
2217
2045
  moduleType={this.props.messageDetails?.type}
2218
- useLocalTemplates={useLocalTemplates}
2219
2046
  />
2220
2047
  )}
2221
2048
  content={(
2222
2049
  <SlideBoxContent
2223
2050
  key="creatives-container-slidebox-content"
2224
- onSelectTemplate={this.props.onSelectTemplate != null ? this.props.onSelectTemplate : this.onSelectTemplate}
2051
+ onSelectTemplate={this.onSelectTemplate}
2225
2052
  onCreateComplete={getCreativesData}
2226
2053
  onPreviewTemplate={this.onPreviewTemplate}
2227
2054
  slidBoxContent={slidBoxContent}
@@ -2297,8 +2124,7 @@ export class Creatives extends React.Component {
2297
2124
  isTestAndPreviewMode={(() => this.state.isTestAndPreviewMode)()}
2298
2125
  onHtmlEditorValidationStateChange={this.updateHtmlEditorValidationState}
2299
2126
  onPersonalizationTokensChange={this.handlePersonalizationTokensChange}
2300
- localTemplatesConfig={pick(this.props.localTemplatesConfig || this.props, constants.LOCAL_TEMPLATE_CONFIG_KEYS)}
2301
- />
2127
+ />
2302
2128
  )}
2303
2129
  footer={this.shouldShowFooter() ? (
2304
2130
  <SlideBoxFooter
@@ -2388,25 +2214,6 @@ Creatives.propTypes = {
2388
2214
  formatMessage: PropTypes.func,
2389
2215
  }),
2390
2216
  stopValidation: PropTypes.func,
2391
- // Local template list (e.g. for SMS fallback): when set, TemplatesV2 uses these instead of Redux.
2392
- // All optional. Pass either localTemplatesConfig (object) or individual props below.
2393
- localTemplatesConfig: PropTypes.shape({
2394
- useLocalTemplates: PropTypes.bool,
2395
- localTemplates: PropTypes.arrayOf(PropTypes.object),
2396
- localTemplatesLoading: PropTypes.bool,
2397
- localTemplatesFilterContent: PropTypes.node,
2398
- localTemplatesSentinelContent: PropTypes.node,
2399
- localTemplatesScrollContainerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
2400
- localTemplatesUseSkeleton: PropTypes.bool,
2401
- }),
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
- onSelectTemplate: PropTypes.func,
2410
2217
  };
2411
2218
  const mapStatesToProps = () => createStructuredSelector({
2412
2219
  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
  }