@capillarytech/creatives-library 9.0.15-alpha.3 → 9.0.16-beta.0

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 (146) hide show
  1. package/AppRoot.js +80 -0
  2. package/app.js +5 -3
  3. package/constants/unified.js +0 -29
  4. package/entry.js +67 -1
  5. package/global-styles.js +1 -1
  6. package/mfe-exposed-components.js +2 -4
  7. package/package.json +2 -2
  8. package/services/api.js +1 -1
  9. package/services/tests/api.test.js +20 -35
  10. package/styles/containers/layout/_layoutPage.scss +8 -6
  11. package/utils/commonUtils.js +1 -19
  12. package/utils/getDataLayer.js +15 -0
  13. package/utils/gtmTrackers/gtmEvents/creativeDetails.js +2 -1
  14. package/utils/mfeDetect.js +1 -0
  15. package/v2Components/CapActionButton/constants.js +1 -8
  16. package/v2Components/CapActionButton/index.js +144 -202
  17. package/v2Components/CapActionButton/index.scss +14 -232
  18. package/v2Components/CapActionButton/messages.js +3 -19
  19. package/v2Components/CapActionButton/tests/index.test.js +18 -42
  20. package/v2Components/CapImageUpload/index.js +2 -2
  21. package/v2Components/CapTagList/index.js +0 -10
  22. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +48 -70
  23. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
  24. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -214
  25. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
  26. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +9 -83
  27. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
  28. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -60
  29. package/v2Components/CommonTestAndPreview/SendTestMessage.js +5 -10
  30. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +22 -202
  31. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +80 -381
  32. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +8 -155
  33. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +0 -11
  34. package/v2Components/CommonTestAndPreview/constants.js +2 -38
  35. package/v2Components/CommonTestAndPreview/index.js +222 -761
  36. package/v2Components/CommonTestAndPreview/messages.js +3 -45
  37. package/v2Components/CommonTestAndPreview/sagas.js +6 -25
  38. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +0 -1
  39. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +267 -284
  40. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +65 -231
  41. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +5 -118
  42. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -341
  43. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +1 -8
  44. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +13 -34
  45. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +283 -281
  46. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
  47. package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -133
  48. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +24 -31
  49. package/v2Components/NavigationBar/index.js +9 -7
  50. package/v2Components/NavigationBar/mfeModuleHeader.config.js +16 -0
  51. package/v2Components/Pagination/_pagination.scss +2 -0
  52. package/v2Components/TemplatePreview/_templatePreview.scss +25 -45
  53. package/v2Components/TemplatePreview/index.js +32 -147
  54. package/v2Components/TemplatePreview/tests/index.test.js +0 -142
  55. package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +6 -6
  56. package/v2Components/TestAndPreviewSlidebox/index.js +1 -13
  57. package/v2Components/TestAndPreviewSlidebox/sagas.js +4 -11
  58. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +1 -3
  59. package/v2Containers/Cap/constants.js +1 -0
  60. package/v2Containers/Cap/index.js +47 -21
  61. package/v2Containers/Cap/sagas.js +19 -0
  62. package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +1 -4
  63. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +0 -4
  64. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +20 -20
  65. package/v2Containers/CreativesContainer/SlideBoxContent.js +7 -37
  66. package/v2Containers/CreativesContainer/SlideBoxFooter.js +5 -14
  67. package/v2Containers/CreativesContainer/SlideBoxHeader.js +5 -36
  68. package/v2Containers/CreativesContainer/constants.js +0 -11
  69. package/v2Containers/CreativesContainer/index.js +104 -323
  70. package/v2Containers/CreativesContainer/index.scss +1 -83
  71. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -79
  72. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
  73. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
  74. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +90 -333
  75. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +15 -20
  76. package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
  77. package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
  78. package/v2Containers/Rcs/constants.js +11 -131
  79. package/v2Containers/Rcs/index.js +829 -2682
  80. package/v2Containers/Rcs/index.scss +8 -443
  81. package/v2Containers/Rcs/messages.js +6 -45
  82. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +39409 -74176
  83. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +74 -10
  84. package/v2Containers/Rcs/tests/index.test.js +40 -151
  85. package/v2Containers/Rcs/tests/mockData.js +0 -38
  86. package/v2Containers/Rcs/tests/utils.test.js +30 -643
  87. package/v2Containers/Rcs/utils.js +12 -466
  88. package/v2Containers/Sms/Create/index.js +48 -115
  89. package/v2Containers/SmsTrai/Create/index.js +4 -9
  90. package/v2Containers/SmsTrai/Edit/constants.js +0 -2
  91. package/v2Containers/SmsTrai/Edit/index.js +169 -678
  92. package/v2Containers/SmsTrai/Edit/messages.js +4 -14
  93. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2854 -5455
  94. package/v2Containers/SmsWrapper/index.js +8 -37
  95. package/v2Containers/TagList/index.js +0 -6
  96. package/v2Containers/Templates/_templates.scss +11 -196
  97. package/v2Containers/Templates/actions.js +0 -11
  98. package/v2Containers/Templates/constants.js +0 -2
  99. package/v2Containers/Templates/index.js +60 -131
  100. package/v2Containers/Templates/sagas.js +13 -57
  101. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1015 -1060
  102. package/v2Containers/Templates/tests/sagas.test.js +16 -199
  103. package/v2Containers/TemplatesV2/TemplatesV2.style.js +10 -75
  104. package/v2Containers/TemplatesV2/index.js +43 -86
  105. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
  106. package/v2Containers/Whatsapp/index.js +25 -4
  107. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -578
  108. package/utils/rcsPayloadUtils.js +0 -92
  109. package/utils/templateVarUtils.js +0 -201
  110. package/utils/tests/rcsPayloadUtils.test.js +0 -226
  111. package/utils/tests/templateVarUtils.test.js +0 -204
  112. package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
  113. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
  114. package/v2Components/CommonTestAndPreview/utils.js +0 -84
  115. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -91
  116. package/v2Components/SmsFallback/constants.js +0 -94
  117. package/v2Components/SmsFallback/index.js +0 -958
  118. package/v2Components/SmsFallback/index.scss +0 -266
  119. package/v2Components/SmsFallback/messages.js +0 -78
  120. package/v2Components/SmsFallback/smsFallbackUtils.js +0 -120
  121. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
  122. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
  123. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
  124. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -208
  125. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -309
  126. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
  127. package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
  128. package/v2Components/TemplatePreview/constants.js +0 -2
  129. package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
  130. package/v2Components/VarSegmentMessageEditor/index.js +0 -125
  131. package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
  132. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -17
  133. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -79
  134. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
  135. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
  136. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
  137. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -227
  138. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -318
  139. package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
  140. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
  141. package/v2Containers/SmsTrai/Edit/index.scss +0 -126
  142. package/v2Containers/Templates/TemplatesActionBar.js +0 -101
  143. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
  144. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
  145. package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
  146. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +0 -131
@@ -44,6 +44,10 @@ class TemplateNameInputField extends React.Component {
44
44
  }
45
45
  }
46
46
  import PropTypes from 'prop-types';
47
+ import {
48
+ CAP_SPACE_16, CAP_SPACE_32, CAP_SPACE_56, CAP_SPACE_64,
49
+ } from '@capillarytech/cap-ui-library/styled/variables';
50
+
47
51
  import CapSlideBox from '@capillarytech/cap-ui-library/CapSlideBox';
48
52
  import CapHeader from '@capillarytech/cap-ui-library/CapHeader';
49
53
  import CapRow from '@capillarytech/cap-ui-library/CapRow';
@@ -53,11 +57,12 @@ import CapNotification from '@capillarytech/cap-ui-library/CapNotification';
53
57
  import { injectIntl, FormattedMessage } from 'react-intl';
54
58
  import classnames from 'classnames';
55
59
  import {
56
- isEmpty, get, forEach, cloneDeep, debounce, pick,
60
+ isEmpty, get, forEach, cloneDeep, debounce,
57
61
  } from 'lodash';
58
62
  import { connect } from 'react-redux';
59
63
  import { createStructuredSelector } from 'reselect';
60
64
  import { bindActionCreators, compose } from 'redux';
65
+ import styled from 'styled-components';
61
66
  import { GA } from '@capillarytech/cap-ui-utils';
62
67
  import { DAEMON } from '@capillarytech/vulcan-react-sdk/utils/sagaInjectorTypes';
63
68
 
@@ -71,7 +76,6 @@ import SlideBoxContent from './SlideBoxContent';
71
76
  import * as constants from './constants';
72
77
  import * as commonUtil from '../../utils/common';
73
78
  import { gtmPush } from '../../utils/gtmTrackers';
74
- import { normalizeRcsMessageContentForApi } from '../../utils/rcsPayloadUtils';
75
79
  import './index.scss';
76
80
  import * as templateActions from '../Templates/actions';
77
81
  import * as globalActions from '../Cap/actions';
@@ -86,10 +90,7 @@ import {
86
90
  } from '../Line/Container/constants';
87
91
  import {EXTERNAL_URL, SITE_URL, WEBPUSH_MEDIA_TYPES} from '../WebPush/constants';
88
92
  import { IMAGE, VIDEO } from '../Facebook/Advertisement/constant';
89
- import { RCS_STATUSES, contentType as RCS_CONTENT_TYPE, EMBEDDED } from '../Rcs/constants';
90
- import { mapRcsCardContentForConsumerWithResolvedTags } from '../Rcs/utils';
91
- import { pickRcsCardVarMappedEntries } from '../Rcs/rcsLibraryHydrationUtils';
92
- import { RCS_SMS_FALLBACK_VAR_MAPPED_PROP } from '../../v2Components/CommonTestAndPreview/constants';
93
+ import { RCS_STATUSES } from '../Rcs/constants';
93
94
  import { CREATIVE } from '../Facebook/constants';
94
95
  import { LOYALTY } from '../App/constants';
95
96
  import {
@@ -130,11 +131,6 @@ import { capSagaForFetchSchemaForEntity, capSagaLiquidEntity } from '../Cap/saga
130
131
  import { v2TemplateSagaWatchGetDefaultBeeTemplates } from '../Templates/sagas';
131
132
  import { DYNAMIC_URL } from '../../v2Components/CapWhatsappCTA/constants';
132
133
  import ErrorInfoNote from '../../v2Components/ErrorInfoNote';
133
- import SlideBoxWrapper from './CreativesSlideBoxWrapper';
134
- import {
135
- computeLiquidFooterUpdateFromFormBuilder,
136
- getSlideBoxWrapperMarginFromLiquidErrors,
137
- } from './embeddedSlideboxUtils';
138
134
 
139
135
  import {
140
136
  transformChannelPayload,
@@ -143,24 +139,51 @@ import {
143
139
  import { MANUAL_CAROUSEL } from '../MobilePushNew/constants';
144
140
  import { BIG_HTML } from '../InApp/constants';
145
141
 
142
+ /**
143
+ * Returns true if value is "deep empty": no errors present.
144
+ * - null/undefined: empty
145
+ * - string: empty if length === 0
146
+ * - array: empty if length === 0
147
+ * - plain object (e.g. { android: [], ios: [], generic: [] }): empty only if every value is deep-empty
148
+ */
149
+ function isDeepEmpty(value) {
150
+ if (value == null) return true;
151
+ if (typeof value === 'string') return value.length === 0;
152
+ if (Array.isArray(value)) return value.length === 0;
153
+ if (typeof value === 'object') {
154
+ return Object.values(value).every(isDeepEmpty);
155
+ }
156
+ return false;
157
+ }
158
+
146
159
  const classPrefix = 'add-creatives-section';
147
160
  const CREATIVES_CONTAINER = 'creativesContainer';
148
161
 
162
+ const SlideBoxWrapper = styled.div`
163
+ .cap-slide-box-v2-container{
164
+ .slidebox-header, .slidebox-content-container{
165
+ margin-bottom: ${({ slideBoxWrapperMargin }) => `${slideBoxWrapperMargin}`};
166
+ padding: 0 rem;
167
+ &.has-footer{
168
+ overflow-x: hidden;
169
+ }
170
+ }
171
+ .slidebox-footer{
172
+ /* Only apply margin-bottom to footer when ErrorInfoNote is shown in footer (BEE editor) */
173
+ /* For HTML Editor, errors are shown in ValidationErrorDisplay (inside content area), so no footer margin needed */
174
+ margin-bottom: ${({ shouldApplyFooterMargin }) => (shouldApplyFooterMargin ? `${CAP_SPACE_16}` : '0')};
175
+ padding: 0 rem;
176
+ &.has-footer{
177
+ overflow-x: hidden;
178
+ }
179
+ }
180
+ }
181
+ `;
149
182
  export class Creatives extends React.Component {
150
183
  constructor(props) {
151
184
  super(props);
152
185
 
153
- const useLocalTemplates = get(
154
- props,
155
- 'localTemplatesConfig.useLocalTemplates',
156
- get(props, 'useLocalTemplates', false),
157
- );
158
- const initialSlidBoxContent = this.getSlideBoxContent({
159
- mode: props.creativesMode,
160
- templateData: props.templateData,
161
- isFullMode: props.isFullMode,
162
- useLocalTemplates,
163
- });
186
+ const initialSlidBoxContent = this.getSlideBoxContent({ mode: props.creativesMode, templateData: props.templateData, isFullMode: props.isFullMode });
164
187
 
165
188
  this.state = {
166
189
  isLoadingContent: true,
@@ -207,13 +230,7 @@ export class Creatives extends React.Component {
207
230
  }
208
231
 
209
232
  componentWillUnmount() {
210
- const isEmbedded = get(this.props, 'location.query.type', '') === EMBEDDED;
211
- const useLocalTemplates = get(
212
- this.props,
213
- 'localTemplatesConfig.useLocalTemplates',
214
- get(this.props, 'useLocalTemplates', false),
215
- );
216
- if (isEmbedded && !useLocalTemplates) {
233
+ if (get(this.props, 'location.query.type', '') === "embedded") {
217
234
  this.props.templateActions.resetTemplateStoreData();
218
235
  }
219
236
  this.props.globalActions.clearMetaEntities();
@@ -823,69 +840,15 @@ export class Creatives extends React.Component {
823
840
  smsFallBackContent = {},
824
841
  creativeName = "",
825
842
  channel = constants.RCS,
826
- rcsCardVarMapped,
827
843
  accountId = "",
828
844
  } = templateData || {};
829
- const { isFullMode: isFullModeForRcsPayload } = this.props;
830
- const isCarouselRcs = (rcsContent?.cardType || '').toString().toUpperCase() === RCS_CONTENT_TYPE.carousel;
831
- const firstCardIn = (rcsContent.cardContent && rcsContent.cardContent[0]) || {};
832
- const {
833
- cardDisplayTitle: _omitDispTitleIn,
834
- cardDisplayDescription: _omitDispDescIn,
835
- ...cardContent
836
- } = firstCardIn;
845
+ const cardContent = (rcsContent.cardContent && rcsContent.cardContent[0]) || {};
837
846
  const Status = RCS_STATUSES.approved || '';
838
- const mergedCardVarMapped = (() => {
839
- const nestedCardVarMapped = cardContent?.cardVarMapped;
840
- const rootMirrorCardVarMapped = rcsCardVarMapped;
841
- const nestedRecord =
842
- nestedCardVarMapped != null && typeof nestedCardVarMapped === 'object'
843
- ? nestedCardVarMapped
844
- : {};
845
- const rootRecord =
846
- rootMirrorCardVarMapped != null && typeof rootMirrorCardVarMapped === 'object'
847
- ? rootMirrorCardVarMapped
848
- : {};
849
- const mergedFromRootAndNested = {
850
- ...pickRcsCardVarMappedEntries(rootRecord),
851
- ...pickRcsCardVarMappedEntries(nestedRecord),
852
- };
853
- return Object.keys(mergedFromRootAndNested).length > 0
854
- ? mergedFromRootAndNested
855
- : null;
856
- })();
857
- // Campaigns (embedded): do not duplicate `cardVarMapped` as root `rcsCardVarMapped` on send —
858
- // slot map stays on `versions…cardContent[0].cardVarMapped` only. Library full mode keeps root mirror.
859
- // Use `=== true` so omitted/undefined `isFullMode` does not behave like library (avoids duplicate on approval payload).
860
- const includeRootRcsCardVarMapped =
861
- mergedCardVarMapped && isFullModeForRcsPayload === true;
862
-
863
- const builtCardContent = isCarouselRcs
864
- ? (rcsContent.cardContent || []).map((card, idx) => {
865
- const {
866
- cardDisplayTitle: _dt,
867
- cardDisplayDescription: _dd,
868
- ...restCard
869
- } = card || {};
870
- return {
871
- ...restCard,
872
- ...(idx === 0 && mergedCardVarMapped ? { cardVarMapped: mergedCardVarMapped } : {}),
873
- Status,
874
- };
875
- })
876
- : [
877
- {
878
- ...cardContent,
879
- ...(mergedCardVarMapped ? { cardVarMapped: mergedCardVarMapped } : {}),
880
- Status,
881
- },
882
- ];
883
847
 
884
848
  creativesTemplateData = {
885
849
  type: channel,
886
850
  edit: true,
887
851
  name: creativeName,
888
- ...(includeRootRcsCardVarMapped ? { rcsCardVarMapped: mergedCardVarMapped } : {}),
889
852
  versions: {
890
853
  base: {
891
854
  content: {
@@ -893,7 +856,12 @@ export class Creatives extends React.Component {
893
856
  rcsContent: {
894
857
  ...rcsContent,
895
858
  ...(accountId && !isFullMode && { accountId }),
896
- cardContent: builtCardContent,
859
+ cardContent: [
860
+ {
861
+ ...cardContent,
862
+ Status,
863
+ },
864
+ ],
897
865
  },
898
866
  smsFallBackContent,
899
867
  },
@@ -1041,10 +1009,7 @@ export class Creatives extends React.Component {
1041
1009
  return newExpandableDetails;
1042
1010
  }
1043
1011
 
1044
- getCreativesData = async (channelParam, template, templateRecords) => { //from creatives to consumers
1045
- const channel = String(
1046
- channelParam || template?.type || get(template, 'value.type') || '',
1047
- ).toUpperCase();
1012
+ getCreativesData = async (channel, template, templateRecords) => { //from creatives to consumers
1048
1013
  let templateData = { channel };
1049
1014
  switch (channel) {
1050
1015
  case constants.SMS:
@@ -1391,104 +1356,28 @@ export class Creatives extends React.Component {
1391
1356
  break;
1392
1357
  case constants.RCS:
1393
1358
  if (template.value) {
1394
- const { isFullMode: isFullModeForRcsConsumerPayload } = this.props;
1395
- const { name = "", versions = {} } = template.value || {};
1396
- const fromSubmit = get(versions, 'base.content.RCS.smsFallBackContent', {});
1397
- const fromRecords = {
1398
- ...(templateRecords?.smsFallBackContent || {}),
1399
- ...(get(templateRecords, 'versions.base.content.RCS.smsFallBackContent') || {}),
1400
- };
1401
- const hasMeaningfulRcsSmsFallback = (smsFallbackPayload) => {
1402
- if (
1403
- !smsFallbackPayload
1404
- || typeof smsFallbackPayload !== 'object'
1405
- || Object.keys(smsFallbackPayload).length === 0
1406
- ) {
1407
- return false;
1408
- }
1409
- const fallbackBodyText = String(
1410
- smsFallbackPayload.smsContent
1411
- ?? smsFallbackPayload.smsTemplateContent
1412
- ?? smsFallbackPayload.message
1413
- ?? smsFallbackPayload.content
1414
- ?? '',
1415
- ).trim();
1416
- const fallbackTemplateName = String(
1417
- smsFallbackPayload.smsTemplateName ?? smsFallbackPayload.templateName ?? '',
1418
- ).trim();
1419
- const rcsSmsFallbackVarMapped =
1420
- smsFallbackPayload?.[RCS_SMS_FALLBACK_VAR_MAPPED_PROP];
1421
- const hasVarMappedEntries =
1422
- rcsSmsFallbackVarMapped != null
1423
- && typeof rcsSmsFallbackVarMapped === 'object'
1424
- && Object.keys(rcsSmsFallbackVarMapped).length > 0;
1425
- return (
1426
- fallbackBodyText !== ''
1427
- || fallbackTemplateName !== ''
1428
- || hasVarMappedEntries
1429
- );
1430
- };
1431
- // If submit has only empty strings, do not let it wipe fallback mirrored on templateRecords (library round-trip).
1432
- const smsFallBackContent = hasMeaningfulRcsSmsFallback(fromSubmit)
1433
- ? { ...fromRecords, ...fromSubmit }
1434
- : { ...fromSubmit, ...fromRecords };
1359
+ const { name = "", versions = {} } = {
1360
+ } = template.value || {};
1361
+ const smsFallBackContent = get(versions, 'base.content.RCS.smsFallBackContent', {});
1435
1362
  const {
1436
- cardContent: cardContentFromSubmit = [],
1363
+ cardContent = [],
1437
1364
  contentType = "",
1438
1365
  cardType = "",
1439
1366
  cardSettings = {},
1440
1367
  accountId = "",
1441
1368
  } = get(versions, 'base.content.RCS.rcsContent', {});
1442
- const rootRcsCardVarMappedFromSubmit = get(template, 'value.rcsCardVarMapped');
1443
- const firstCardFromSubmit = Array.isArray(cardContentFromSubmit)
1444
- ? cardContentFromSubmit[0]
1445
- : null;
1446
- const cardContent = mapRcsCardContentForConsumerWithResolvedTags(
1447
- cardContentFromSubmit,
1448
- rootRcsCardVarMappedFromSubmit,
1449
- isFullModeForRcsConsumerPayload,
1450
- );
1451
1369
  const rcsContent = {
1452
1370
  contentType,
1453
1371
  cardType,
1454
1372
  cardSettings,
1455
1373
  cardContent,
1456
1374
  };
1457
- const cardVarMappedFromFirstRcsCard =
1458
- firstCardFromSubmit?.cardVarMapped != null
1459
- && typeof firstCardFromSubmit.cardVarMapped === 'object'
1460
- ? pickRcsCardVarMappedEntries(firstCardFromSubmit.cardVarMapped)
1461
- : null;
1462
- const includeRootRcsCardVarMappedOnConsumerPayload =
1463
- cardVarMappedFromFirstRcsCard
1464
- && Object.keys(cardVarMappedFromFirstRcsCard).length > 0
1465
- && isFullModeForRcsConsumerPayload === true;
1466
1375
  templateData = {
1467
1376
  channel,
1468
1377
  creativeName: name,
1469
1378
  rcsContent,
1470
1379
  accountId,
1471
- ...(includeRootRcsCardVarMappedOnConsumerPayload
1472
- ? { rcsCardVarMapped: cardVarMappedFromFirstRcsCard }
1473
- : {}),
1474
1380
  };
1475
- // Library / campaign consumers round-trip templateData via getTemplateData; include SMS fallback
1476
- // so reopening the editor restores fallback text and tag mappings.
1477
- // cap-campaigns-v2 API expects `smsFallBackContent.message` (see normalizeRcsMessageContentForApi).
1478
- if (hasMeaningfulRcsSmsFallback(smsFallBackContent)) {
1479
- const smsText =
1480
- smsFallBackContent.message
1481
- ?? smsFallBackContent.smsContent
1482
- ?? smsFallBackContent.smsTemplateContent
1483
- ?? '';
1484
- templateData.smsFallBackContent = {
1485
- ...smsFallBackContent,
1486
- ...(String(smsText).trim() !== ''
1487
- ? { message: String(smsText).trim() }
1488
- : {}),
1489
- };
1490
- }
1491
- normalizeRcsMessageContentForApi(templateData);
1492
1381
  }
1493
1382
  break;
1494
1383
  case constants.ZALO:
@@ -1606,10 +1495,7 @@ export class Creatives extends React.Component {
1606
1495
  return templateData;
1607
1496
  };
1608
1497
 
1609
- getSlideBoxContent({ mode, templateData, isFullMode, useLocalTemplates }) {
1610
- if (useLocalTemplates && mode === 'create' && isEmpty(templateData)) {
1611
- return 'templates';
1612
- }
1498
+ getSlideBoxContent({ mode, templateData, isFullMode }) {
1613
1499
  let creativesMode = isFullMode ? 'createTemplate' : 'templates';// for library mode templates page is initial mode and for full mode createTemplates
1614
1500
  if (mode === 'create' && isFullMode) {
1615
1501
  creativesMode = 'createTemplate';
@@ -1697,110 +1583,24 @@ export class Creatives extends React.Component {
1697
1583
  getFormData = (template) => {
1698
1584
  // Always reset isGetFormData so the child does not re-send form data on every re-render
1699
1585
  // (e.g. when user fixes validation error by typing, we must not auto-close the slidebox)
1700
- this.setState(
1701
- (prevState) => {
1702
- const next = { isGetFormData: false };
1703
- if (!template.validity) {
1704
- return next;
1705
- }
1706
- const baseTd = prevState.templateData || template;
1707
- const channel = (
1708
- baseTd?.type
1709
- || template?.type
1710
- || get(template, 'value.type')
1711
- || ''
1712
- ).toUpperCase();
1713
- // Library mode: persist last submitted creatives shape so reopening still hydrates the editor
1714
- // (parent may not merge getCreativesData back into templateData).
1715
- if (this.props.isFullMode === false && template.value) {
1716
- if (channel === constants.RCS) {
1717
- const smsFallBackFromPayload = get(
1718
- template.value,
1719
- 'versions.base.content.RCS.smsFallBackContent',
1720
- );
1721
- const rcsCardVarMappedFromPayload = get(
1722
- template.value,
1723
- 'versions.base.content.RCS.rcsContent.cardContent[0].cardVarMapped',
1724
- );
1725
- next.templateData = {
1726
- ...baseTd,
1727
- type: constants.RCS,
1728
- name: template?.value?.name,
1729
- versions: template?.value?.versions,
1730
- ...(smsFallBackFromPayload != null
1731
- && typeof smsFallBackFromPayload === 'object'
1732
- && Object.keys(smsFallBackFromPayload).length > 0
1733
- ? { smsFallBackContent: smsFallBackFromPayload }
1734
- : {}),
1735
- ...(rcsCardVarMappedFromPayload != null
1736
- && typeof rcsCardVarMappedFromPayload === 'object'
1737
- ? { rcsCardVarMapped: rcsCardVarMappedFromPayload }
1738
- : {}),
1739
- };
1740
- if (template._id) {
1741
- next.templateData._id = template._id;
1742
- }
1743
- } else if (channel === constants.SMS) {
1744
- const submittedSmsTemplateValue = template?.value;
1745
- const smsVersions =
1746
- submittedSmsTemplateValue?.history != null
1747
- ? submittedSmsTemplateValue
1748
- : {
1749
- base: submittedSmsTemplateValue?.base,
1750
- history: submittedSmsTemplateValue?.base
1751
- ? [submittedSmsTemplateValue.base]
1752
- : [],
1753
- };
1754
- next.templateData = {
1755
- ...baseTd,
1756
- type: constants.SMS,
1757
- name: baseTd?.name || constants.DEFAULT_SMS_TEMPLATE_NAME,
1758
- versions: smsVersions,
1759
- };
1760
- if (template?._id) {
1761
- next.templateData._id = template._id;
1762
- }
1763
- }
1764
- }
1765
- return next;
1766
- },
1767
- () => {
1768
- if (!template.validity) {
1769
- return;
1770
- }
1771
- const templateData = this.state.templateData ? this.state.templateData : template; //select existing or create new content
1772
- const channelForConsumer = String(
1773
- templateData.type
1774
- || template.type
1775
- || get(template, 'value.type')
1776
- || '',
1777
- ).toUpperCase();
1778
- const creativesData = this.getCreativesData(
1779
- channelForConsumer,
1780
- template,
1781
- this.state.templateData || template,
1782
- );// convers data to consumer understandable format
1783
- creativesData.then((data) => {
1784
- this.logGTMEvent(channelForConsumer, data);
1785
- this.processCentralCommsMetaId(channelForConsumer, data, {
1786
- closeSlideBoxAfterSubmit: template.closeSlideBoxAfterSubmit,
1586
+ this.setState({ isGetFormData: false });
1587
+ if (template.validity) {
1588
+ this.setState(
1589
+ {},
1590
+ () => {
1591
+ const templateData = this.state.templateData ? this.state.templateData : template; //select existing or create new content
1592
+ const channel = templateData.type;
1593
+ const creativesData = this.getCreativesData(channel, template, templateData);// convers data to consumer understandable format
1594
+ creativesData.then((data) => {
1595
+ this.logGTMEvent(channel, data);
1596
+ this.processCentralCommsMetaId(channel, data);
1787
1597
  });
1788
- });
1789
- },
1790
- );
1598
+ },
1599
+ );
1600
+ }
1791
1601
  }
1792
1602
 
1793
- processCentralCommsMetaId = (channel, creativesData, options = {}) => {
1794
- const { closeSlideBoxAfterSubmit = false } = options;
1795
- const maybeCloseLibrarySlideBox = () => {
1796
- if (
1797
- closeSlideBoxAfterSubmit
1798
- && this.props.isFullMode === false
1799
- && typeof this.handleCloseSlideBox === 'function'
1800
- ) {
1801
- this.handleCloseSlideBox();
1802
- }
1803
- };
1603
+ processCentralCommsMetaId = (channel, creativesData) => {
1804
1604
  // Create the payload for the centralcommnsmetaId API call
1805
1605
  const { isLoyaltyModule = false, loyaltyMetaData = {} } = this.props;
1806
1606
  const { actionName, setMetaData = () => { } } = loyaltyMetaData;
@@ -1826,7 +1626,6 @@ export class Creatives extends React.Component {
1826
1626
  if (result?.status?.code === 200) {
1827
1627
  setMetaData(result);
1828
1628
  this.props.getCreativesData(creativesData);
1829
- maybeCloseLibrarySlideBox();
1830
1629
  } else {
1831
1630
  CapNotification.error({ message: <FormattedMessage {...messages.somethingWentWrong} /> });
1832
1631
  }
@@ -1837,7 +1636,6 @@ export class Creatives extends React.Component {
1837
1636
  } else {
1838
1637
  // If not a loyalty module or different action, should work as usual
1839
1638
  this.props.getCreativesData(creativesData);
1840
- maybeCloseLibrarySlideBox();
1841
1639
  }
1842
1640
  };
1843
1641
 
@@ -1870,9 +1668,7 @@ export class Creatives extends React.Component {
1870
1668
  }
1871
1669
  this.setState((prevState) => ({
1872
1670
  ...prevState,
1873
- // Library mode (isFullMode === false): retain last template so reopening still has RCS payload.
1874
- // Undefined isFullMode defaults to full-mode close behavior (clear templateData).
1875
- ...(this.props.isFullMode !== false ? { templateData: undefined } : {}),
1671
+ templateData: undefined,
1876
1672
  showSlideBox: false,
1877
1673
  liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
1878
1674
  isLiquidValidationError: false,
@@ -2075,17 +1871,21 @@ export class Creatives extends React.Component {
2075
1871
  }
2076
1872
 
2077
1873
  showLiquidErrorInFooter = (errorMessagesFromFormBuilder, currentFormBuilderTab) => {
2078
- const next = computeLiquidFooterUpdateFromFormBuilder(
2079
- errorMessagesFromFormBuilder,
2080
- this.state.liquidErrorMessage,
2081
- currentFormBuilderTab,
2082
- {
2083
- previousIsLiquidValidationError: this.state.isLiquidValidationError,
2084
- currentChannelUpper: this.state.currentChannel?.toUpperCase(),
2085
- },
2086
- );
2087
- if (next == null) return;
2088
- this.setState(next);
1874
+ const liquidMsgs = get(errorMessagesFromFormBuilder, constants.LIQUID_ERROR_MSG, []);
1875
+ const standardMsgs = get(errorMessagesFromFormBuilder, constants.STANDARD_ERROR_MSG, []);
1876
+ const hasLiquid = !isDeepEmpty(liquidMsgs);
1877
+ const hasStandard = !isDeepEmpty(standardMsgs);
1878
+ const isLiquidValidationError = hasLiquid || hasStandard;
1879
+ // Don't overwrite existing liquid error with empty only for Mobile Push OLD (FormBuilder/clear calls empty there); SMS/others clear on input change
1880
+ const isMobilePush = this.state.currentChannel?.toUpperCase() === constants.MOBILE_PUSH;
1881
+ if (!hasLiquid && !hasStandard && this.state.isLiquidValidationError && isMobilePush) {
1882
+ return;
1883
+ }
1884
+ this.setState({
1885
+ isLiquidValidationError,
1886
+ liquidErrorMessage: errorMessagesFromFormBuilder,
1887
+ activeFormBuilderTab: currentFormBuilderTab === 1 ? constants.ANDROID : (currentFormBuilderTab === 2 ? constants.IOS : null), // Update activeFormBuilderTab, default to 1 if undefined
1888
+ });
2089
1889
  }
2090
1890
 
2091
1891
  // Callback to update HTML Editor validation state (called from EmailWrapper)
@@ -2208,11 +2008,6 @@ export class Creatives extends React.Component {
2208
2008
  inAppEditorType,
2209
2009
  htmlEditorValidationState,
2210
2010
  } = this.state;
2211
- const useLocalTemplates = get(
2212
- this.props,
2213
- 'localTemplatesConfig.useLocalTemplates',
2214
- get(this.props, 'useLocalTemplates', false),
2215
- );
2216
2011
  const {
2217
2012
  isFullMode,
2218
2013
  creativesMode,
@@ -2231,6 +2026,7 @@ export class Creatives extends React.Component {
2231
2026
  smsRegister,
2232
2027
  enableNewChannels,
2233
2028
  eventContextTags,
2029
+ waitEventContextTags = {},
2234
2030
  isLoyaltyModule,
2235
2031
  loyaltyMetaData = {},
2236
2032
  } = this.props;
@@ -2264,7 +2060,14 @@ export class Creatives extends React.Component {
2264
2060
  // IMPORTANT: Never show ErrorInfoNote in footer when in HTML Editor mode, even if liquidErrorMessage exists
2265
2061
  const shouldShowErrorInfoNoteInFooter = isHTMLEditorMode ? false : hasBEEEditorErrors;
2266
2062
 
2267
- const slideBoxWrapperMargin = getSlideBoxWrapperMarginFromLiquidErrors(liquidErrorMessage);
2063
+ // Calculate margin for header/content (always apply if there are errors, regardless of editor type)
2064
+ const slideBoxWrapperMargin = (get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0 && get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0)
2065
+ ? CAP_SPACE_64
2066
+ : get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0
2067
+ ? CAP_SPACE_56
2068
+ : get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0
2069
+ ? CAP_SPACE_32
2070
+ : 0;
2268
2071
  /* TODO: Instead of passing down same props separately to each component down, write common function to these props and pass it accordingly */
2269
2072
 
2270
2073
  // Compute anonymous user type and channel restrictions
@@ -2293,10 +2096,7 @@ export class Creatives extends React.Component {
2293
2096
  <SlideBoxWrapper
2294
2097
  slideBoxWrapperMargin={slideBoxWrapperMargin}
2295
2098
  shouldApplyFooterMargin={shouldShowErrorInfoNoteInFooter}
2296
- className={classnames(
2297
- `${classPrefix} ${isFullMode ? 'creatives-full-mode' : 'creatives-library-mode'} ${mapTemplateCreate ? 'map-template-create' : ''}`,
2298
- useLocalTemplates && slidBoxContent === 'templates' && 'creatives-slidebox--local-sms-templates',
2299
- )}
2099
+ className={classnames(`${classPrefix} ${isFullMode ? 'creatives-full-mode' : 'creatives-library-mode'} ${mapTemplateCreate ? 'map-template-create' : ''}`)}
2300
2100
  >
2301
2101
  <CapSlideBox
2302
2102
  header={
@@ -2321,13 +2121,12 @@ export class Creatives extends React.Component {
2321
2121
  smsRegister={smsRegister}
2322
2122
  handleClose={this.handleCloseSlideBox}
2323
2123
  moduleType={this.props.messageDetails?.type}
2324
- useLocalTemplates={useLocalTemplates}
2325
2124
  />
2326
2125
  )}
2327
2126
  content={(
2328
2127
  <SlideBoxContent
2329
2128
  key="creatives-container-slidebox-content"
2330
- onSelectTemplate={this.props.onSelectTemplate != null ? this.props.onSelectTemplate : this.onSelectTemplate}
2129
+ onSelectTemplate={this.onSelectTemplate}
2331
2130
  onCreateComplete={getCreativesData}
2332
2131
  onPreviewTemplate={this.onPreviewTemplate}
2333
2132
  slidBoxContent={slidBoxContent}
@@ -2395,6 +2194,7 @@ export class Creatives extends React.Component {
2395
2194
  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.
2396
2195
  hostName={this.props?.hostName || ''}
2397
2196
  eventContextTags={eventContextTags}
2197
+ waitEventContextTags={waitEventContextTags}
2398
2198
  isLoyaltyModule={isLoyaltyModule}
2399
2199
  loyaltyMetaData={loyaltyMetaData}
2400
2200
  showTestAndPreviewSlidebox={showTestAndPreviewSlidebox}
@@ -2403,8 +2203,7 @@ export class Creatives extends React.Component {
2403
2203
  isTestAndPreviewMode={(() => this.state.isTestAndPreviewMode)()}
2404
2204
  onHtmlEditorValidationStateChange={this.updateHtmlEditorValidationState}
2405
2205
  onPersonalizationTokensChange={this.handlePersonalizationTokensChange}
2406
- localTemplatesConfig={pick(this.props.localTemplatesConfig || this.props, constants.LOCAL_TEMPLATE_CONFIG_KEYS)}
2407
- />
2206
+ />
2408
2207
  )}
2409
2208
  footer={this.shouldShowFooter() ? (
2410
2209
  <SlideBoxFooter
@@ -2489,31 +2288,13 @@ Creatives.propTypes = {
2489
2288
  orgUnitId: PropTypes.number,
2490
2289
  hostName: PropTypes.string,
2491
2290
  eventContextTags: PropTypes.array,
2291
+ waitEventContextTags: PropTypes.object,
2492
2292
  loyaltyTagFetchingDependencies: PropTypes.object,
2493
2293
  customerType: PropTypes.string,
2494
2294
  intl: PropTypes.shape({
2495
2295
  formatMessage: PropTypes.func,
2496
2296
  }),
2497
2297
  stopValidation: PropTypes.func,
2498
- // Local template list (e.g. for SMS fallback): when set, TemplatesV2 uses these instead of Redux.
2499
- // All optional. Pass either localTemplatesConfig (object) or individual props below.
2500
- localTemplatesConfig: PropTypes.shape({
2501
- useLocalTemplates: PropTypes.bool,
2502
- localTemplates: PropTypes.arrayOf(PropTypes.object),
2503
- localTemplatesLoading: PropTypes.bool,
2504
- localTemplatesFilterContent: PropTypes.node,
2505
- localTemplatesSentinelContent: PropTypes.node,
2506
- localTemplatesScrollContainerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
2507
- localTemplatesUseSkeleton: PropTypes.bool,
2508
- }),
2509
- useLocalTemplates: PropTypes.bool,
2510
- localTemplates: PropTypes.arrayOf(PropTypes.object),
2511
- localTemplatesLoading: PropTypes.bool,
2512
- localTemplatesFilterContent: PropTypes.node,
2513
- localTemplatesSentinelContent: PropTypes.node,
2514
- localTemplatesScrollContainerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
2515
- localTemplatesUseSkeleton: PropTypes.bool,
2516
- onSelectTemplate: PropTypes.func,
2517
2298
  };
2518
2299
  const mapStatesToProps = () => createStructuredSelector({
2519
2300
  isLoading: isLoadingSelector(),