@capillarytech/creatives-library 8.0.359-alpha.0 → 8.0.359-alpha.1

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 (147) hide show
  1. package/constants/unified.js +29 -0
  2. package/index.html +1 -0
  3. package/package.json +1 -1
  4. package/services/tests/api.test.js +35 -20
  5. package/utils/cdnTransformation.js +75 -3
  6. package/utils/commonUtils.js +19 -1
  7. package/utils/rcsPayloadUtils.js +92 -0
  8. package/utils/templateVarUtils.js +201 -0
  9. package/utils/tests/cdnTransformation.test.js +127 -0
  10. package/utils/tests/rcsPayloadUtils.test.js +226 -0
  11. package/utils/tests/templateVarUtils.test.js +204 -0
  12. package/v2Components/CapActionButton/constants.js +7 -0
  13. package/v2Components/CapActionButton/index.js +166 -108
  14. package/v2Components/CapActionButton/index.scss +157 -6
  15. package/v2Components/CapActionButton/messages.js +19 -3
  16. package/v2Components/CapActionButton/tests/index.test.js +41 -17
  17. package/v2Components/CapImageUpload/index.js +2 -2
  18. package/v2Components/CapTagList/index.js +10 -0
  19. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +72 -49
  20. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
  21. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +214 -21
  22. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
  23. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +83 -9
  24. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
  25. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +79 -11
  26. package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
  27. package/v2Components/CommonTestAndPreview/UnifiedPreview/PreviewHeader.js +16 -0
  28. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +157 -15
  29. package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberPreviewContent.js +14 -132
  30. package/v2Components/CommonTestAndPreview/UnifiedPreview/WebPushPreviewContent.js +169 -0
  31. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +400 -239
  32. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +202 -10
  33. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
  34. package/v2Components/CommonTestAndPreview/constants.js +40 -2
  35. package/v2Components/CommonTestAndPreview/index.js +887 -453
  36. package/v2Components/CommonTestAndPreview/messages.js +45 -3
  37. package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
  38. package/v2Components/CommonTestAndPreview/sagas.js +25 -6
  39. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +308 -284
  40. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +231 -65
  41. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
  42. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
  43. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
  44. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +34 -13
  45. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/PreviewHeader.test.js +163 -0
  46. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
  47. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/ViberPreviewContent.test.js +0 -364
  48. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/WebPushPreviewContent.test.js +522 -0
  49. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +454 -1
  50. package/v2Components/CommonTestAndPreview/tests/constants.test.js +2 -1
  51. package/v2Components/CommonTestAndPreview/tests/index.test.js +327 -4
  52. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
  53. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +31 -24
  54. package/v2Components/FormBuilder/index.js +167 -56
  55. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +91 -0
  56. package/v2Components/SmsFallback/constants.js +73 -0
  57. package/v2Components/SmsFallback/index.js +956 -0
  58. package/v2Components/SmsFallback/index.scss +265 -0
  59. package/v2Components/SmsFallback/messages.js +78 -0
  60. package/v2Components/SmsFallback/smsFallbackUtils.js +119 -0
  61. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
  62. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
  63. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
  64. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +223 -0
  65. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +309 -0
  66. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
  67. package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
  68. package/v2Components/TemplatePreview/_templatePreview.scss +37 -22
  69. package/v2Components/TemplatePreview/constants.js +2 -0
  70. package/v2Components/TemplatePreview/index.js +143 -31
  71. package/v2Components/TemplatePreview/tests/index.test.js +142 -0
  72. package/v2Components/TestAndPreviewSlidebox/index.js +15 -3
  73. package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
  74. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
  75. package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
  76. package/v2Components/VarSegmentMessageEditor/index.js +125 -0
  77. package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
  78. package/v2Containers/App/constants.js +3 -0
  79. package/v2Containers/App/tests/constants.test.js +61 -0
  80. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +17 -0
  81. package/v2Containers/CreativesContainer/SlideBoxContent.js +36 -4
  82. package/v2Containers/CreativesContainer/SlideBoxFooter.js +14 -5
  83. package/v2Containers/CreativesContainer/SlideBoxHeader.js +36 -5
  84. package/v2Containers/CreativesContainer/constants.js +9 -0
  85. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +79 -0
  86. package/v2Containers/CreativesContainer/index.js +382 -127
  87. package/v2Containers/CreativesContainer/index.scss +83 -1
  88. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
  89. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +79 -34
  90. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +79 -16
  91. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
  92. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +357 -98
  93. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
  94. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
  95. package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
  96. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
  97. package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
  98. package/v2Containers/Rcs/constants.js +120 -11
  99. package/v2Containers/Rcs/index.js +2577 -812
  100. package/v2Containers/Rcs/index.scss +281 -8
  101. package/v2Containers/Rcs/messages.js +34 -3
  102. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +225 -0
  103. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +98036 -70145
  104. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
  105. package/v2Containers/Rcs/tests/index.test.js +152 -121
  106. package/v2Containers/Rcs/tests/mockData.js +38 -0
  107. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +318 -0
  108. package/v2Containers/Rcs/tests/utils.test.js +646 -30
  109. package/v2Containers/Rcs/utils.js +478 -11
  110. package/v2Containers/Sms/Create/index.js +106 -40
  111. package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
  112. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  113. package/v2Containers/SmsTrai/Create/index.js +9 -4
  114. package/v2Containers/SmsTrai/Edit/constants.js +2 -0
  115. package/v2Containers/SmsTrai/Edit/index.js +640 -130
  116. package/v2Containers/SmsTrai/Edit/index.scss +121 -0
  117. package/v2Containers/SmsTrai/Edit/messages.js +14 -4
  118. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4328 -2375
  119. package/v2Containers/SmsWrapper/index.js +37 -8
  120. package/v2Containers/TagList/index.js +6 -0
  121. package/v2Containers/Templates/TemplatesActionBar.js +101 -0
  122. package/v2Containers/Templates/_templates.scss +166 -86
  123. package/v2Containers/Templates/actions.js +11 -0
  124. package/v2Containers/Templates/constants.js +2 -0
  125. package/v2Containers/Templates/index.js +203 -145
  126. package/v2Containers/Templates/sagas.js +62 -13
  127. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
  128. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1062 -1017
  129. package/v2Containers/Templates/tests/sagas.test.js +222 -22
  130. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
  131. package/v2Containers/Templates/tests/webpush.test.js +375 -0
  132. package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
  133. package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
  134. package/v2Containers/TemplatesV2/index.js +86 -23
  135. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
  136. package/v2Containers/Viber/constants.js +0 -19
  137. package/v2Containers/Viber/index.js +47 -714
  138. package/v2Containers/Viber/index.scss +0 -148
  139. package/v2Containers/Viber/messages.js +0 -116
  140. package/v2Containers/Viber/tests/index.test.js +0 -80
  141. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
  142. package/v2Containers/WebPush/Create/index.js +91 -8
  143. package/v2Containers/WebPush/Create/index.scss +7 -0
  144. package/v2Containers/WebPush/Create/tests/getTemplateContent.test.js +348 -0
  145. package/v2Containers/WebPush/Create/tests/testAndPreviewIntegration.test.js +325 -0
  146. package/v2Containers/Whatsapp/index.js +3 -20
  147. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
@@ -0,0 +1,46 @@
1
+ @import '~@capillarytech/cap-ui-library/styles/_variables';
2
+
3
+ /* Same look as RCS edit message block: background, spacing, text color */
4
+ .rcs_text_area_wrapper {
5
+ .rcs-edit-template-message-input {
6
+ background-color: $CAP_G10;
7
+ padding: $CAP_SPACE_12 $CAP_SPACE_16 $CAP_SPACE_16;
8
+ }
9
+
10
+ .rcs-edit-template-message-split {
11
+ margin: 0 0 $CAP_SPACE_04 0;
12
+ overflow: hidden;
13
+ text-overflow: ellipsis;
14
+ color: $FONT_COLOR_04;
15
+ font-weight: 500;
16
+ }
17
+
18
+ /* Variable chips: match RCS edit (white field, light border, 4px radius) */
19
+ .rcs-edit-template-message-input .ant-input,
20
+ .rcs-edit-template-message-input textarea.ant-input {
21
+ margin: 0 0 0.125rem 0;
22
+ border-radius: 0.25rem;
23
+ border: 0.0625rem solid $CAP_G07;
24
+ background-color: $CAP_WHITE;
25
+ overflow: hidden;
26
+ }
27
+
28
+ /* Small gap between tag border and the next line (static text) */
29
+ .rcs-edit-template-message-input :not(:first-child) {
30
+ margin-top: $CAP_SPACE_08;
31
+ }
32
+
33
+ .rcs-edit-template-message-input > *:last-child {
34
+ margin-bottom: 0;
35
+ }
36
+
37
+ .var-segment-message-editor__var-slot {
38
+ display: flex;
39
+ flex-direction: column;
40
+ width: 100%;
41
+ }
42
+ }
43
+
44
+ .var-segment-message-editor__read-only-value {
45
+ margin: 0;
46
+ }
@@ -124,3 +124,6 @@ export const LOYALTY = 'loyalty';
124
124
  export const FAILURE = 'FAILURE';
125
125
  export const DATE_DISPLAY_FORMAT = 'D MMM YYYY';
126
126
  export const TIME_DISPLAY_FORMAT = 'hh:mm A';
127
+ export const EXTERNAL_URL = 'EXTERNAL_URL';
128
+ export const URL = 'URL';
129
+ export const SITE_URL = 'SITE_URL';
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Tests for App container constants
3
+ *
4
+ * Covers newly added URL type constants: EXTERNAL_URL, URL, SITE_URL
5
+ */
6
+
7
+ import {
8
+ EXTERNAL_URL,
9
+ URL,
10
+ SITE_URL,
11
+ LOYALTY,
12
+ FAILURE,
13
+ DATE_DISPLAY_FORMAT,
14
+ TIME_DISPLAY_FORMAT,
15
+ } from '../constants';
16
+
17
+ describe('App constants', () => {
18
+ describe('URL type constants', () => {
19
+ it('should export EXTERNAL_URL with correct value', () => {
20
+ expect(EXTERNAL_URL).toBe('EXTERNAL_URL');
21
+ });
22
+
23
+ it('should export URL with correct value', () => {
24
+ expect(URL).toBe('URL');
25
+ });
26
+
27
+ it('should export SITE_URL with correct value', () => {
28
+ expect(SITE_URL).toBe('SITE_URL');
29
+ });
30
+
31
+ it('should have distinct values for all three URL constants', () => {
32
+ expect(EXTERNAL_URL).not.toBe(URL);
33
+ expect(EXTERNAL_URL).not.toBe(SITE_URL);
34
+ expect(URL).not.toBe(SITE_URL);
35
+ });
36
+
37
+ it('should be strings', () => {
38
+ expect(typeof EXTERNAL_URL).toBe('string');
39
+ expect(typeof URL).toBe('string');
40
+ expect(typeof SITE_URL).toBe('string');
41
+ });
42
+ });
43
+
44
+ describe('Other existing constants still exported correctly', () => {
45
+ it('should export LOYALTY', () => {
46
+ expect(LOYALTY).toBe('loyalty');
47
+ });
48
+
49
+ it('should export FAILURE', () => {
50
+ expect(FAILURE).toBe('FAILURE');
51
+ });
52
+
53
+ it('should export DATE_DISPLAY_FORMAT', () => {
54
+ expect(DATE_DISPLAY_FORMAT).toBe('D MMM YYYY');
55
+ });
56
+
57
+ it('should export TIME_DISPLAY_FORMAT', () => {
58
+ expect(TIME_DISPLAY_FORMAT).toBe('hh:mm A');
59
+ });
60
+ });
61
+ });
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import { CAP_SPACE_16 } from '@capillarytech/cap-ui-library/styled/variables';
3
+
4
+ const CreativesSlideBoxWrapper = ({ slideBoxWrapperMargin, shouldApplyFooterMargin, children, className, ...rest }) => (
5
+ <div
6
+ className={`creatives-slide-box-wrapper${className ? ` ${className}` : ''}`}
7
+ style={{
8
+ '--slidebox-wrapper-margin': slideBoxWrapperMargin,
9
+ '--slidebox-footer-margin': shouldApplyFooterMargin ? `${CAP_SPACE_16}` : '0',
10
+ }}
11
+ {...rest}
12
+ >
13
+ {children}
14
+ </div>
15
+ );
16
+
17
+ export default CreativesSlideBoxWrapper;
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
3
3
  import styled from 'styled-components';
4
4
  import get from 'lodash/get';
5
5
  import isEmpty from 'lodash/isEmpty';
6
+ import pick from 'lodash/pick';
6
7
  import cloneDeep from 'lodash/cloneDeep';
7
8
  import TemplatesV2 from '../TemplatesV2';
8
9
  import TemplatePreview from '../../v2Components/TemplatePreview';
@@ -25,6 +26,7 @@ import Viber from '../Viber';
25
26
  import Whatsapp from '../Whatsapp';
26
27
  import InApp from '../InApp';
27
28
  import Rcs from '../Rcs';
29
+ import { isRcsTextOnlyCardMediaType, resolveRcsCardPreviewStrings } from '../Rcs/utils';
28
30
  import { getWhatsappContent } from '../Whatsapp/utils';
29
31
  import * as commonUtil from '../../utils/common';
30
32
  import Zalo from '../Zalo';
@@ -180,6 +182,8 @@ export function SlideBoxContent(props) {
180
182
  isTestAndPreviewMode,
181
183
  onHtmlEditorValidationStateChange,
182
184
  } = props;
185
+ const localTemplatesConfig = props.localTemplatesConfig || pick(props, constants.LOCAL_TEMPLATE_CONFIG_KEYS);
186
+ const useLocalTemplates = !!get(localTemplatesConfig, 'useLocalTemplates');
183
187
  const type = (messageDetails.type || '').toLowerCase(); // type is context in get tags values : outbound | dvs | referral | loyalty | coupons
184
188
  const query = { type: !isFullMode && 'embedded', module: isFullMode ? 'default' : 'library', isEditFromCampaigns: (templateData || {}).isEditFromCampaigns};
185
189
  const creativesLocationProps = {
@@ -399,12 +403,37 @@ export function SlideBoxContent(props) {
399
403
  }
400
404
  case constants.RCS: {
401
405
  const template = cloneDeep(templateDataObject);
402
- const { description = "", media: { mediaUrl = "" } = {}, title = "", suggestions = [] } = get(template, 'versions.base.content.RCS.rcsContent.cardContent[0]', {});
406
+ const cardPath = 'versions.base.content.RCS.rcsContent.cardContent[0]';
407
+ const card = get(template, cardPath, {}) || {};
408
+ const {
409
+ description = '',
410
+ media: { mediaUrl = '' } = {},
411
+ title = '',
412
+ mediaType: cardMediaType,
413
+ suggestions = [],
414
+ cardVarMapped: nestedCardVarMapped,
415
+ } = card;
416
+ const rootMirror = templateDataObject?.rcsCardVarMapped;
417
+ const nestedRecord =
418
+ nestedCardVarMapped != null && typeof nestedCardVarMapped === 'object'
419
+ ? nestedCardVarMapped
420
+ : {};
421
+ const rootRecord =
422
+ rootMirror != null && typeof rootMirror === 'object' ? rootMirror : {};
423
+ const mergedCardVarMapped = { ...rootRecord, ...nestedRecord };
424
+ const textOnlyCard = isRcsTextOnlyCardMediaType(cardMediaType);
425
+ const { rcsTitle, rcsDesc } = resolveRcsCardPreviewStrings(
426
+ title,
427
+ description,
428
+ mergedCardVarMapped,
429
+ !isFullMode,
430
+ textOnlyCard,
431
+ );
403
432
  return {
404
433
  rcsPreviewContent: {
405
434
  rcsImageSrc: mediaUrl,
406
- rcsTitle: title,
407
- rcsDesc: description,
435
+ rcsTitle,
436
+ rcsDesc,
408
437
  ...(suggestions.length > 0 && {
409
438
  buttonText: suggestions[0]?.text,
410
439
  }),
@@ -430,7 +459,7 @@ export function SlideBoxContent(props) {
430
459
 
431
460
  return (
432
461
  <CreativesWrapper>
433
- {!isFullMode && slidBoxContent === 'templates' && (
462
+ {slidBoxContent === 'templates' && (!isFullMode || useLocalTemplates) && (
434
463
  <TemplatesV2
435
464
  isFullMode={isFullMode}
436
465
  onSelectTemplate={onSelectTemplate}
@@ -463,6 +492,7 @@ export function SlideBoxContent(props) {
463
492
  waitEventContextTags={waitEventContextTags}
464
493
  loyaltyMetaData={loyaltyMetaData}
465
494
  isLoyaltyModule={isLoyaltyModule}
495
+ localTemplatesConfig={localTemplatesConfig}
466
496
  />
467
497
  )}
468
498
  {isPreview && (
@@ -633,6 +663,7 @@ export function SlideBoxContent(props) {
633
663
  route={{ name: 'sms' }}
634
664
  isCreateSms={isCreateSms}
635
665
  isComponent
666
+ templateData={templateData}
636
667
  isGetFormData={isGetFormData}
637
668
  getFormSubscriptionData={getFormData}
638
669
  getLiquidTags={getLiquidTags}
@@ -1239,6 +1270,7 @@ export function SlideBoxContent(props) {
1239
1270
  )}
1240
1271
  {isCreateRcs && (<Rcs
1241
1272
  {...rcsCommonProps}
1273
+ templateData={templateData}
1242
1274
  showLiquidErrorInFooter={showLiquidErrorInFooter}
1243
1275
  showTestAndPreviewSlidebox={showTestAndPreviewSlidebox}
1244
1276
  handleTestAndPreview={handleTestAndPreview}
@@ -49,6 +49,8 @@ function SlideBoxFooter(props) {
49
49
  isAnonymousType = false,
50
50
  templateData = {},
51
51
  hasPersonalizationTokenError: hasPersonalizationTokenErrorProp = false,
52
+ /** When set (e.g. SMS library create), overrides `creativesTemplatesSave` (“Done”) for the primary button */
53
+ primarySaveButtonMessage,
52
54
  } = props;
53
55
  // Calculate if buttons should be disabled
54
56
  // Only apply validation state checks for EMAIL channel in HTML Editor mode (not BEE/DragDrop)
@@ -165,6 +167,12 @@ function SlideBoxFooter(props) {
165
167
  // Use prop from parent (FormBuilder flow) when available; else fall back to templateData check
166
168
  const hasPersonalizationTokenError = hasPersonalizationTokenErrorProp === true || (restrictPersonalization && hasPersonalizationTokens());
167
169
 
170
+ function getSaveButtonLabel() {
171
+ if (primarySaveButtonMessage) return <FormattedMessage {...primarySaveButtonMessage} />;
172
+ if (isFullMode) return getFullModeSaveBtn(slidBoxContent, isCreatingTemplate);
173
+ return <FormattedMessage {...messages.creativesTemplatesSave} />;
174
+ }
175
+
168
176
  return (
169
177
  <div className="template-footer-width">
170
178
  {shouldShowErrorInfoNote && (
@@ -187,11 +195,7 @@ function SlideBoxFooter(props) {
187
195
  onClick={onSave}
188
196
  disabled={isTemplateNameEmpty || fetchingCmsData || shouldDisableButtons || hasPersonalizationTokenError}
189
197
  >
190
- {isFullMode ? (
191
- getFullModeSaveBtn(slidBoxContent, isCreatingTemplate)
192
- ) : (
193
- <FormattedMessage {...messages.creativesTemplatesSave} />
194
- )}
198
+ {getSaveButtonLabel()}
195
199
  </CapButton>
196
200
  {showTestAndPreviewButton && (
197
201
  <CapButton
@@ -264,6 +268,10 @@ SlideBoxFooter.propTypes = {
264
268
  templateData: PropTypes.object,
265
269
  formData: PropTypes.array,
266
270
  hasPersonalizationTokenError: PropTypes.bool,
271
+ primarySaveButtonMessage: PropTypes.shape({
272
+ id: PropTypes.string,
273
+ defaultMessage: PropTypes.string,
274
+ }),
267
275
  };
268
276
 
269
277
  SlideBoxFooter.defaultProps = {
@@ -291,5 +299,6 @@ SlideBoxFooter.defaultProps = {
291
299
  selectedEmailCreateMode: '',
292
300
  formData: [],
293
301
  hasPersonalizationTokenError: false,
302
+ primarySaveButtonMessage: undefined,
294
303
  };
295
304
  export default SlideBoxFooter;
@@ -16,6 +16,7 @@ import { isTraiDLTEnable } from '../../utils/common';
16
16
  import { formatString } from '../../utils/Formatter';
17
17
  import {
18
18
  CAP_SPACE_12,
19
+ CAP_SPACE_16,
19
20
  } from '@capillarytech/cap-ui-library/styled/variables';
20
21
  import { WHATSAPP_HELP_DOC_LINK, JOURNEY } from './constants';
21
22
 
@@ -24,7 +25,7 @@ const StyledLabel = styled(CapLabelInline)`
24
25
  margin-right: ${CAP_SPACE_12};
25
26
  `;
26
27
  const PrefixWrapper = styled.div`
27
- margin-right: 16px;
28
+ margin-right: ${CAP_SPACE_16};
28
29
  `;
29
30
  const renderData = (type, value, channel) => (
30
31
  <StyledLabel className={channel?.toLowerCase() === ZALO ? 'zalo-template-name-spacing' : ''} type={type}>
@@ -33,7 +34,25 @@ const renderData = (type, value, channel) => (
33
34
  );
34
35
 
35
36
  export function SlideBoxHeader(props) {
36
- const { slidBoxContent, templateData, onShowTemplates, creativesMode, isFullMode, showPrefix, shouldShowTemplateName, channel, templateNameRenderProp, weChatTemplateType, onWeChatMaptemplateStepChange, weChatMaptemplateStep, templateStep, smsRegister, handleClose, moduleType } = props;
37
+ const {
38
+ slidBoxContent,
39
+ templateData,
40
+ onShowTemplates,
41
+ creativesMode,
42
+ isFullMode,
43
+ showPrefix,
44
+ shouldShowTemplateName,
45
+ channel,
46
+ templateNameRenderProp,
47
+ weChatTemplateType,
48
+ onWeChatMaptemplateStepChange,
49
+ weChatMaptemplateStep,
50
+ templateStep,
51
+ smsRegister,
52
+ handleClose,
53
+ moduleType,
54
+ useLocalTemplates = false,
55
+ } = props;
37
56
  const showTemplateNameHeader = isFullMode && shouldShowTemplateName;
38
57
  const mapTemplateCreate = !showTemplateNameHeader && slidBoxContent === 'createTemplate' && weChatTemplateType === MAP_TEMPLATE && templateStep !== 'modeSelection';
39
58
  const isTraiDlt = isTraiDLTEnable(isFullMode, smsRegister);
@@ -54,7 +73,7 @@ export function SlideBoxHeader(props) {
54
73
  } = templateData || {};
55
74
  const templateName = whatsappTemplateName || templateData?.name;
56
75
  const showRcsTemplateName = channel.toLowerCase() === RCS && slidBoxContent === 'editTemplate' && !isFullMode && templateName;
57
- const showTemplateName = isWhatsappEdit || showRcsTemplateName || isZaloEdit;
76
+ const showTemplateName = isWhatsappEdit || isZaloEdit;
58
77
  const whatsappCategory = whatsappTemplateCategory || get(templateData, `versions.base.content.whatsapp.category`, '');
59
78
  const whatsappLanguageCode = whatsappTemplateLanguageCode || get(templateData, `versions.base.content.whatsapp.languages[0].language`, '');
60
79
 
@@ -81,6 +100,9 @@ export function SlideBoxHeader(props) {
81
100
  window.open(WHATSAPP_HELP_DOC_LINK, '_blank');
82
101
  };
83
102
 
103
+ const showCreativesTemplatesBackButton =
104
+ !isFullMode && (moduleType === JOURNEY || useLocalTemplates);
105
+
84
106
  return (
85
107
  <div key="creatives-container-slidebox-header-content">
86
108
  {slidBoxContent === 'templates' && !showTemplateNameHeader && (
@@ -89,7 +111,7 @@ export function SlideBoxHeader(props) {
89
111
  description={![NO_COMMUNICATION, FTP].includes(channel) &&
90
112
  <FormattedMessage {...messages.creativeTemplatesDesc} />
91
113
  }
92
- prefix={!isFullMode && moduleType === JOURNEY &&
114
+ prefix={showCreativesTemplatesBackButton &&
93
115
  <PrefixWrapper>
94
116
  <CapIcons.backIcon onClick={handleClose} />
95
117
  </PrefixWrapper>
@@ -123,6 +145,12 @@ export function SlideBoxHeader(props) {
123
145
  <StyledLabel className='whatsapp-rcs-slidebox-template-name' type="label2">{templateName}</StyledLabel>
124
146
  </>
125
147
  ) : ""}
148
+ {showRcsTemplateName && (
149
+ <>
150
+ {renderData("label1", <FormattedMessage {...globalMessages.creativeNameLabel} />, channel)}
151
+ <StyledLabel className='whatsapp-rcs-slidebox-template-name' type="label2">{templateName}</StyledLabel>
152
+ </>
153
+ )}
126
154
  {isWhatsappEdit && (
127
155
  <>
128
156
  {renderData("label3", <FormattedMessage {...whatsppMsg.labelSeperator} />)}
@@ -135,7 +163,7 @@ export function SlideBoxHeader(props) {
135
163
  }
136
164
  </>
137
165
  }
138
- prefix={creativesMode !== 'edit' && !isFullMode && showPrefix &&
166
+ prefix={!isFullMode && showPrefix &&
139
167
  <PrefixWrapper>
140
168
  <CapIcons.backIcon onClick={onShowTemplates} />
141
169
  </PrefixWrapper>
@@ -191,5 +219,8 @@ SlideBoxHeader.propTypes = {
191
219
  shouldShowTemplateName: PropTypes.bool,
192
220
  templateNameRenderProp: PropTypes.func,
193
221
  smsRegister: PropTypes.any,
222
+ handleClose: PropTypes.func,
223
+ moduleType: PropTypes.string,
224
+ useLocalTemplates: PropTypes.bool,
194
225
  };
195
226
  export default SlideBoxHeader;
@@ -67,3 +67,12 @@ export const ALLOWED_CHANNELS_FOR_ANONYMOUS = ['mobilepush', 'webpush'];
67
67
  export const ALL_CHANNELS_NEW = [
68
68
  'sms', 'email', 'whatsapp', 'facebook', 'line', 'viber', 'rcs', 'zalo', 'inapp', 'call_task', 'ftp',
69
69
  ];
70
+
71
+ export const LOCAL_TEMPLATE_CONFIG_KEYS = ['useLocalTemplates', 'localTemplates', 'localTemplatesLoading', 'localTemplatesFilterContent', 'localTemplatesUseSkeleton', 'localTemplatesOnPageChange'];
72
+
73
+ /** Keys passed from parents into `Templates` when using local SMS template list (extends `LOCAL_TEMPLATE_CONFIG_KEYS`). */
74
+ export const LOCAL_TEMPLATE_CONFIG_KEYS_FOR_PICK = [
75
+ ...LOCAL_TEMPLATE_CONFIG_KEYS,
76
+ 'localTemplatesLoadingTip',
77
+ 'localTemplatesFooterContent',
78
+ ];
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Shared logic for CreativesContainer slidebox + embedded flows (e.g. RCS SMS fallback)
3
+ * that mirror the same footer liquid errors and layout margins.
4
+ */
5
+ import get from 'lodash/get';
6
+ import {
7
+ CAP_SPACE_32,
8
+ CAP_SPACE_56,
9
+ CAP_SPACE_64,
10
+ } from '@capillarytech/cap-ui-library/styled/variables';
11
+ import * as constants from './constants';
12
+
13
+ /**
14
+ * Returns true if value is "deep empty": no errors present.
15
+ * Same rules as CreativesContainer (used for liquid / standard error payloads).
16
+ */
17
+ export function isDeepEmpty(value) {
18
+ if (value == null) return true;
19
+ if (typeof value === 'string') return value.length === 0;
20
+ if (Array.isArray(value)) return value.every(isDeepEmpty);
21
+ if (typeof value === 'object') {
22
+ return Object.values(value).every(isDeepEmpty);
23
+ }
24
+ return false;
25
+ }
26
+
27
+ /**
28
+ * Header/content margin below slidebox chrome when ErrorInfoNote stacks errors — same formula as CreativesContainer#render.
29
+ */
30
+ export function getSlideBoxWrapperMarginFromLiquidErrors(liquidErrorMessage) {
31
+ return (get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0
32
+ && get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0)
33
+ ? CAP_SPACE_64
34
+ : get(liquidErrorMessage, 'LIQUID_ERROR_MSG.length', 0) > 0
35
+ ? CAP_SPACE_56
36
+ : get(liquidErrorMessage, 'STANDARD_ERROR_MSG.length', 0) > 0
37
+ ? CAP_SPACE_32
38
+ : 0;
39
+ }
40
+
41
+ /**
42
+ * Maps FormBuilder `showLiquidErrorInFooter` args to slidebox footer state.
43
+ * Returns `null` when CreativesContainer intentionally skips updating (Mobile Push OLD empty clear).
44
+ */
45
+ export function computeLiquidFooterUpdateFromFormBuilder(
46
+ errorMessagesFromFormBuilder,
47
+ existingLiquidErrorMessageOrCurrentFormBuilderTab,
48
+ currentFormBuilderTabOrOptions,
49
+ maybeOptions,
50
+ ) {
51
+ // Backward compatible signature handling:
52
+ // old: (errors, currentFormBuilderTab, options)
53
+ // new: (errors, existingLiquidErrorMessage, currentFormBuilderTab, options)
54
+ const hasExistingErrorMessageObject = existingLiquidErrorMessageOrCurrentFormBuilderTab
55
+ && typeof existingLiquidErrorMessageOrCurrentFormBuilderTab === 'object'
56
+ && !Array.isArray(existingLiquidErrorMessageOrCurrentFormBuilderTab);
57
+ const currentFormBuilderTab = hasExistingErrorMessageObject
58
+ ? currentFormBuilderTabOrOptions
59
+ : existingLiquidErrorMessageOrCurrentFormBuilderTab;
60
+ const options = hasExistingErrorMessageObject ? maybeOptions : currentFormBuilderTabOrOptions;
61
+ const { previousIsLiquidValidationError, currentChannelUpper } = options || {};
62
+ const liquidMsgs = get(errorMessagesFromFormBuilder, constants.LIQUID_ERROR_MSG, []);
63
+ const standardMsgs = get(errorMessagesFromFormBuilder, constants.STANDARD_ERROR_MSG, []);
64
+ const hasLiquid = !isDeepEmpty(liquidMsgs);
65
+ const hasStandard = !isDeepEmpty(standardMsgs);
66
+ const isLiquidValidationError = hasLiquid || hasStandard;
67
+ const isMobilePush = currentChannelUpper === constants.MOBILE_PUSH;
68
+ if (!hasLiquid && !hasStandard && previousIsLiquidValidationError && isMobilePush) {
69
+ return null;
70
+ }
71
+ return {
72
+ isLiquidValidationError,
73
+ liquidErrorMessage: errorMessagesFromFormBuilder,
74
+ activeFormBuilderTab:
75
+ currentFormBuilderTab === 1
76
+ ? constants.ANDROID
77
+ : (currentFormBuilderTab === 2 ? constants.IOS : null),
78
+ };
79
+ }