@capillarytech/creatives-library 8.0.271 → 8.0.272

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 (149) hide show
  1. package/assets/Android.png +0 -0
  2. package/assets/iOS.png +0 -0
  3. package/constants/unified.js +2 -1
  4. package/initialReducer.js +2 -0
  5. package/package.json +1 -1
  6. package/services/api.js +10 -0
  7. package/services/tests/api.test.js +34 -0
  8. package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +17 -35
  9. package/tests/integration/TemplateCreation/api-response.js +31 -1
  10. package/tests/integration/TemplateCreation/msw-handler.js +2 -0
  11. package/utils/common.js +5 -0
  12. package/utils/commonUtils.js +28 -5
  13. package/utils/tests/commonUtil.test.js +224 -0
  14. package/utils/transformTemplateConfig.js +0 -10
  15. package/v2Components/CapDeviceContent/index.js +61 -56
  16. package/v2Components/CapTagList/index.js +6 -1
  17. package/v2Components/CapTagListWithInput/index.js +5 -1
  18. package/v2Components/CapTagListWithInput/messages.js +1 -1
  19. package/v2Components/CapWhatsappCTA/tests/index.test.js +5 -0
  20. package/v2Components/ErrorInfoNote/constants.js +1 -0
  21. package/v2Components/ErrorInfoNote/index.js +402 -72
  22. package/v2Components/ErrorInfoNote/messages.js +32 -6
  23. package/v2Components/ErrorInfoNote/style.scss +278 -6
  24. package/v2Components/FormBuilder/tests/index.test.js +13 -4
  25. package/v2Components/HtmlEditor/HTMLEditor.js +418 -99
  26. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +870 -0
  27. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1882 -133
  28. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +27 -16
  29. package/v2Components/HtmlEditor/_htmlEditor.scss +108 -45
  30. package/v2Components/HtmlEditor/_index.lazy.scss +0 -1
  31. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +23 -102
  32. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +148 -140
  33. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +2 -1
  34. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
  35. package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +9 -1
  36. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +31 -6
  37. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +22 -0
  38. package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +4 -7
  39. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +35 -45
  40. package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +1 -3
  41. package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
  42. package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +7 -6
  43. package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +7 -10
  44. package/v2Components/HtmlEditor/components/PreviewPane/index.js +22 -43
  45. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  46. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +18 -0
  47. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +36 -31
  48. package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +46 -34
  49. package/v2Components/HtmlEditor/components/ValidationPanel/constants.js +6 -0
  50. package/v2Components/HtmlEditor/components/ValidationPanel/index.js +52 -46
  51. package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +277 -0
  52. package/v2Components/HtmlEditor/components/ValidationTabs/index.js +295 -0
  53. package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +51 -0
  54. package/v2Components/HtmlEditor/constants.js +45 -20
  55. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +373 -16
  56. package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +351 -16
  57. package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
  58. package/v2Components/HtmlEditor/hooks/useInAppContent.js +88 -146
  59. package/v2Components/HtmlEditor/hooks/useValidation.js +213 -56
  60. package/v2Components/HtmlEditor/index.js +1 -1
  61. package/v2Components/HtmlEditor/messages.js +102 -94
  62. package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +214 -45
  63. package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +134 -0
  64. package/v2Components/HtmlEditor/utils/contentSanitizer.js +40 -41
  65. package/v2Components/HtmlEditor/utils/htmlValidator.js +71 -72
  66. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +158 -124
  67. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +23 -25
  68. package/v2Components/HtmlEditor/utils/validationAdapter.js +66 -41
  69. package/v2Components/HtmlEditor/utils/validationConstants.js +38 -0
  70. package/v2Components/MobilePushPreviewV2/constants.js +6 -0
  71. package/v2Components/MobilePushPreviewV2/index.js +33 -7
  72. package/v2Components/TemplatePreview/_templatePreview.scss +55 -24
  73. package/v2Components/TemplatePreview/index.js +47 -32
  74. package/v2Components/TemplatePreview/messages.js +4 -0
  75. package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +1 -0
  76. package/v2Containers/BeeEditor/index.js +172 -90
  77. package/v2Containers/BeePopupEditor/_beePopupEditor.scss +14 -0
  78. package/v2Containers/BeePopupEditor/constants.js +10 -0
  79. package/v2Containers/BeePopupEditor/index.js +194 -0
  80. package/v2Containers/BeePopupEditor/tests/index.test.js +627 -0
  81. package/v2Containers/CreativesContainer/SlideBoxContent.js +127 -51
  82. package/v2Containers/CreativesContainer/SlideBoxFooter.js +156 -13
  83. package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -1
  84. package/v2Containers/CreativesContainer/constants.js +1 -0
  85. package/v2Containers/CreativesContainer/index.js +251 -47
  86. package/v2Containers/CreativesContainer/messages.js +8 -0
  87. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +11 -2
  88. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +38 -50
  89. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +103 -0
  90. package/v2Containers/Email/actions.js +7 -0
  91. package/v2Containers/Email/constants.js +5 -1
  92. package/v2Containers/Email/index.js +234 -29
  93. package/v2Containers/Email/messages.js +32 -0
  94. package/v2Containers/Email/reducer.js +12 -1
  95. package/v2Containers/Email/sagas.js +61 -7
  96. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +2 -0
  97. package/v2Containers/Email/tests/reducer.test.js +46 -0
  98. package/v2Containers/Email/tests/sagas.test.js +320 -29
  99. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1246 -0
  100. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +212 -21
  101. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +40 -74
  102. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +2472 -0
  103. package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +520 -0
  104. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +2 -67
  105. package/v2Containers/EmailWrapper/constants.js +2 -0
  106. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +627 -79
  107. package/v2Containers/EmailWrapper/index.js +103 -23
  108. package/v2Containers/EmailWrapper/messages.js +65 -1
  109. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +955 -0
  110. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +596 -82
  111. package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +376 -0
  112. package/v2Containers/InApp/__tests__/sagas.test.js +363 -0
  113. package/v2Containers/InApp/actions.js +7 -0
  114. package/v2Containers/InApp/constants.js +20 -4
  115. package/v2Containers/InApp/index.js +802 -360
  116. package/v2Containers/InApp/index.scss +4 -3
  117. package/v2Containers/InApp/messages.js +7 -3
  118. package/v2Containers/InApp/reducer.js +21 -3
  119. package/v2Containers/InApp/sagas.js +29 -9
  120. package/v2Containers/InApp/selectors.js +25 -5
  121. package/v2Containers/InApp/tests/index.test.js +154 -50
  122. package/v2Containers/InApp/tests/reducer.test.js +34 -0
  123. package/v2Containers/InApp/tests/sagas.test.js +61 -9
  124. package/v2Containers/InApp/tests/selectors.test.js +612 -0
  125. package/v2Containers/InAppWrapper/components/InAppWrapperView.js +151 -0
  126. package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +267 -0
  127. package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +23 -0
  128. package/v2Containers/InAppWrapper/constants.js +16 -0
  129. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +473 -0
  130. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +198 -0
  131. package/v2Containers/InAppWrapper/index.js +148 -0
  132. package/v2Containers/InAppWrapper/messages.js +49 -0
  133. package/v2Containers/InappAdvance/index.js +1099 -0
  134. package/v2Containers/InappAdvance/index.scss +10 -0
  135. package/v2Containers/InappAdvance/tests/index.test.js +448 -0
  136. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -0
  137. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -0
  138. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -0
  139. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -0
  140. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -0
  141. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -0
  142. package/v2Containers/TagList/index.js +62 -19
  143. package/v2Containers/Templates/_templates.scss +60 -1
  144. package/v2Containers/Templates/index.js +89 -4
  145. package/v2Containers/Templates/messages.js +4 -0
  146. package/v2Containers/TemplatesV2/TemplatesV2.style.js +4 -2
  147. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -0
  148. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +0 -152
  149. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +0 -214
@@ -16,7 +16,7 @@ const VALIDATION_SOURCES = {
16
16
  EMAIL_SPECIFIC: 'email-specific',
17
17
  INAPP_SPECIFIC: 'inapp-specific',
18
18
  EMAIL_COMPATIBILITY: 'email-compatibility',
19
- MOBILE_OPTIMIZATION: 'mobile-optimization'
19
+ MOBILE_OPTIMIZATION: 'mobile-optimization',
20
20
  };
21
21
 
22
22
  /**
@@ -30,42 +30,47 @@ export const transformValidationToErrorInfo = (validation, variant = 'email') =>
30
30
  return { errorMessages: { LIQUID_ERROR_MSG: [], STANDARD_ERROR_MSG: [] } };
31
31
  }
32
32
 
33
+ if (typeof validation.getAllIssues !== 'function') {
34
+ console.warn('[validationAdapter] validation.getAllIssues is not a function:', validation);
35
+ return { errorMessages: { LIQUID_ERROR_MSG: [], STANDARD_ERROR_MSG: [] } };
36
+ }
37
+
33
38
  const allIssues = validation.getAllIssues();
39
+ // Safety check: ensure allIssues is an array
40
+ if (!Array.isArray(allIssues)) {
41
+ console.warn('[validationAdapter] validation.getAllIssues() did not return an array:', allIssues);
42
+ return { errorMessages: { LIQUID_ERROR_MSG: [], STANDARD_ERROR_MSG: [] } };
43
+ }
34
44
 
35
45
  // Separate liquid and standard errors
36
- const liquidErrors = allIssues.filter(issue =>
37
- issue.source === VALIDATION_SOURCES.LIQUID ||
38
- issue.rule?.includes('liquid') ||
39
- issue.message?.toLowerCase().includes('liquid')
40
- );
41
-
42
- const standardErrors = allIssues.filter(issue =>
43
- issue.source !== VALIDATION_SOURCES.LIQUID &&
44
- !issue.rule?.includes('liquid') &&
45
- !issue.message?.toLowerCase().includes('liquid')
46
- );
46
+ const liquidErrors = allIssues.filter((issue) => issue.source === VALIDATION_SOURCES.LIQUID
47
+ || issue.rule?.includes('liquid')
48
+ || issue.message?.toLowerCase().includes('liquid'));
49
+
50
+ const standardErrors = allIssues.filter((issue) => issue.source !== VALIDATION_SOURCES.LIQUID
51
+ && !issue.rule?.includes('liquid')
52
+ && !issue.message?.toLowerCase().includes('liquid'));
47
53
 
48
54
  // Format errors for ErrorInfoNote
49
- const formatErrors = (errors) =>
50
- errors.map(error => {
51
- let message = error.message;
52
-
53
- // Add line/column info if available
54
- if (error.line) {
55
- message += ` Line ${error.line}`;
56
- if (error.column) {
57
- message += `, Char ${error.column}`;
58
- }
59
- message += '.';
55
+ const formatErrors = (errors) => errors.map((error) => {
56
+ let {message} = error;
57
+
58
+ // Add line/column info if available
59
+ if (error.line) {
60
+ message += ` Line ${error.line}`;
61
+ if (error.column) {
62
+ message += `, Char ${error.column}`;
60
63
  }
64
+ message += '.';
65
+ }
61
66
 
62
- // Add rule info if available
63
- if (error.rule) {
64
- message += ` • ${error.rule}`;
65
- }
67
+ // Add rule info if available
68
+ if (error.rule) {
69
+ message += ` • ${error.rule}`;
70
+ }
66
71
 
67
- return message;
68
- });
72
+ return message;
73
+ });
69
74
 
70
75
  // Handle InApp variant with platform-specific errors (if needed in future)
71
76
  if (variant === 'inapp') {
@@ -74,8 +79,8 @@ export const transformValidationToErrorInfo = (validation, variant = 'email') =>
74
79
  return {
75
80
  errorMessages: {
76
81
  LIQUID_ERROR_MSG: formatErrors(liquidErrors),
77
- STANDARD_ERROR_MSG: formatErrors(standardErrors)
78
- }
82
+ STANDARD_ERROR_MSG: formatErrors(standardErrors),
83
+ },
79
84
  };
80
85
  }
81
86
 
@@ -83,8 +88,8 @@ export const transformValidationToErrorInfo = (validation, variant = 'email') =>
83
88
  return {
84
89
  errorMessages: {
85
90
  LIQUID_ERROR_MSG: formatErrors(liquidErrors),
86
- STANDARD_ERROR_MSG: formatErrors(standardErrors)
87
- }
91
+ STANDARD_ERROR_MSG: formatErrors(standardErrors),
92
+ },
88
93
  };
89
94
  };
90
95
 
@@ -98,7 +103,18 @@ export const hasValidationErrors = (validation) => {
98
103
  return false;
99
104
  }
100
105
 
106
+ if (typeof validation.getAllIssues !== 'function') {
107
+ console.warn('[validationAdapter] validation.getAllIssues is not a function:', validation);
108
+ return false;
109
+ }
110
+
101
111
  const allIssues = validation.getAllIssues();
112
+ // Safety check: ensure allIssues is an array
113
+ if (!Array.isArray(allIssues)) {
114
+ console.warn('[validationAdapter] validation.getAllIssues() did not return an array:', allIssues);
115
+ return false;
116
+ }
117
+
102
118
  return allIssues.length > 0;
103
119
  };
104
120
 
@@ -112,19 +128,28 @@ export const getValidationSummary = (validation) => {
112
128
  return { totalErrors: 0, totalWarnings: 0, hasLiquidErrors: false };
113
129
  }
114
130
 
131
+ if (typeof validation.getAllIssues !== 'function') {
132
+ console.warn('[validationAdapter] validation.getAllIssues is not a function:', validation);
133
+ return { totalErrors: 0, totalWarnings: 0, hasLiquidErrors: false };
134
+ }
135
+
115
136
  const allIssues = validation.getAllIssues();
116
- const errors = allIssues.filter(issue => issue.severity === 'error');
117
- const warnings = allIssues.filter(issue => issue.severity === 'warning');
118
- const liquidErrors = allIssues.filter(issue =>
119
- issue.source === VALIDATION_SOURCES.LIQUID ||
120
- issue.rule?.includes('liquid') ||
121
- issue.message?.toLowerCase().includes('liquid')
122
- );
137
+ // Safety check: ensure allIssues is an array
138
+ if (!Array.isArray(allIssues)) {
139
+ console.warn('[validationAdapter] validation.getAllIssues() did not return an array:', allIssues);
140
+ return { totalErrors: 0, totalWarnings: 0, hasLiquidErrors: false };
141
+ }
142
+
143
+ const errors = allIssues.filter((issue) => issue.severity === 'error');
144
+ const warnings = allIssues.filter((issue) => issue.severity === 'warning');
145
+ const liquidErrors = allIssues.filter((issue) => issue.source === VALIDATION_SOURCES.LIQUID
146
+ || issue.rule?.includes('liquid')
147
+ || issue.message?.toLowerCase().includes('liquid'));
123
148
 
124
149
  return {
125
150
  totalErrors: errors.length,
126
151
  totalWarnings: warnings.length,
127
152
  hasLiquidErrors: liquidErrors.length > 0,
128
- totalIssues: allIssues.length
153
+ totalIssues: allIssues.length,
129
154
  };
130
155
  };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Validation Constants
3
+ *
4
+ * Shared constants for validation issue categorization across HTML Editor components
5
+ */
6
+
7
+ // Issue sources for categorizing validation errors
8
+ export const ISSUE_SOURCES = {
9
+ HTMLHINT: 'htmlhint',
10
+ CSS_VALIDATOR: 'css-validator',
11
+ CUSTOM: 'custom',
12
+ SECURITY: 'security',
13
+ LIQUID: 'liquid-validator',
14
+ };
15
+
16
+ // Label issue patterns - syntax errors related to tags
17
+ // These patterns identify tag syntax errors (open/close tags, attributes, brackets)
18
+ export const LABEL_ISSUE_PATTERNS = [
19
+ 'tag must be paired',
20
+ 'open tag match failed',
21
+ 'closed tag match failed',
22
+ 'missing required',
23
+ 'tag-pair',
24
+ 'attr-value-not-empty',
25
+ 'attr-no-duplication',
26
+ 'tag-self-close',
27
+ 'spec-char-escape',
28
+ 'tagname-lowercase',
29
+ 'attr-lowercase',
30
+ 'src-not-empty',
31
+ 'alt-require',
32
+ ];
33
+
34
+ // Tab keys: Errors = blocking, Warnings = non-blocking
35
+ export const ERROR_TAB_KEYS = {
36
+ ERRORS: 'errors',
37
+ WARNINGS: 'warnings',
38
+ };
@@ -0,0 +1,6 @@
1
+ export const DEVICE_TYPES = {
2
+ ANDROID: 'android',
3
+ IOS: 'ios',
4
+ };
5
+
6
+ export const IPHONE = 'iphone';
@@ -16,6 +16,7 @@ import { INAPP } from '../../v2Containers/App/constants';
16
16
  import { ANDROID, IOS } from '../../v2Containers/InApp/constants';
17
17
  import { getCtaObject } from '../../v2Containers/InApp/utils';
18
18
  import { CAROUSEL, VIDEO } from '../../v2Containers/MobilePushNew/constants';
19
+ import { DEVICE_TYPES, IPHONE } from './constants';
19
20
 
20
21
  class MobilePushPreviewV2 extends React.Component { // eslint-disable-line react/prefer-stateless-function
21
22
  constructor(props) {
@@ -26,8 +27,17 @@ class MobilePushPreviewV2 extends React.Component { // eslint-disable-line react
26
27
  this.getPreview = this.getPreview.bind(this);
27
28
  this.setContent = this.setContent.bind(this);
28
29
  this.goToDuplicate = this.goToDuplicate.bind(this);
29
- const hasAndroid = get(props, 'templateData.versions.base.ANDROID') || get(props, 'templateData.androidContent.title');
30
- const hasIos = get(props, 'templateData.versions.base.IOS') || get(props, 'templateData.iosContent.title');
30
+ // Check for INAPP channel structure (versions.base.content.ANDROID/IOS) or MOBILE_PUSH structure (versions.base.ANDROID/IOS)
31
+ const { channel, templateData } = props;
32
+ let hasAndroid;
33
+ let hasIos;
34
+ if (channel === INAPP.toUpperCase()) {
35
+ hasAndroid = get(templateData, 'versions.base.content.ANDROID') || get(templateData, 'androidContent');
36
+ hasIos = get(templateData, 'versions.base.content.IOS') || get(templateData, 'iosContent');
37
+ } else {
38
+ hasAndroid = get(templateData, 'versions.base.ANDROID') || get(templateData, 'androidContent.title');
39
+ hasIos = get(templateData, 'versions.base.IOS') || get(templateData, 'iosContent.title');
40
+ }
31
41
  let device = "android";
32
42
  if (hasAndroid) {
33
43
  device = 'android';
@@ -45,6 +55,17 @@ class MobilePushPreviewV2 extends React.Component { // eslint-disable-line react
45
55
  if (channel === INAPP.toUpperCase()) {
46
56
  const androidContent = get(templateData, 'versions.base.content.ANDROID') || get(templateData, 'androidContent');
47
57
  const iosContent = get(templateData, 'versions.base.content.IOS') || get(templateData, 'iosContent');
58
+ const isBeeFreeTemplate = get(androidContent, 'isBEEeditor') || get(iosContent, 'isBEEeditor');
59
+ if (isBeeFreeTemplate) {
60
+ // Normalize device to 'android' or 'ios' for comparison
61
+ const normalizedDevice = device === IPHONE ? DEVICE_TYPES.IOS : device?.toLowerCase();
62
+ const isAndroid = normalizedDevice === ANDROID.toLowerCase();
63
+ content = {
64
+ inAppPreviewContent: isAndroid ? androidContent?.beeHtml : iosContent?.beeHtml,
65
+ isBeeFreeTemplate: true,
66
+ };
67
+ return content;
68
+ }
48
69
  const androidPreviewContent = {
49
70
  templateTitle: androidContent?.title,
50
71
  templateMsg: androidContent?.message,
@@ -57,9 +78,12 @@ class MobilePushPreviewV2 extends React.Component { // eslint-disable-line react
57
78
  mediaPreview: { inAppImageSrcIos: iosContent?.expandableDetails?.image },
58
79
  ctaData: getCtaObject(iosContent?.expandableDetails?.ctas),
59
80
  };
81
+ // Normalize device to 'android' or 'ios' for comparison
82
+ const normalizedDevice = device === IPHONE ? DEVICE_TYPES.IOS : device?.toLowerCase();
83
+ const isAndroid = normalizedDevice === ANDROID.toLowerCase();
60
84
  content = {
61
- inAppPreviewContent: device === ANDROID.toLowerCase() ? androidPreviewContent : iosPreviewContent,
62
- templateLayoutType: device === ANDROID.toLowerCase() ? androidContent?.bodyType : iosContent?.bodyType,
85
+ inAppPreviewContent: isAndroid ? androidPreviewContent : iosPreviewContent,
86
+ templateLayoutType: isAndroid ? androidContent?.bodyType : iosContent?.bodyType,
63
87
  };
64
88
  } else if (channel === MOBILE_PUSH) {
65
89
  const androidContent = get(templateData, 'versions.base.ANDROID') || get(templateData, 'androidContent');
@@ -125,7 +149,9 @@ class MobilePushPreviewV2 extends React.Component { // eslint-disable-line react
125
149
  }
126
150
 
127
151
  getPreview(device) {
128
- const deviceParam = device === ANDROID.toLowerCase() ? ANDROID : IOS;
152
+ // Normalize device to 'android' or 'ios' for comparison
153
+ const normalizedDevice = device === IPHONE ? DEVICE_TYPES.IOS : device?.toLowerCase();
154
+ const deviceParam = normalizedDevice === ANDROID.toLowerCase() ? ANDROID : IOS;
129
155
  return (
130
156
  <TemplatePreview
131
157
  device={this.props.channel === INAPP.toUpperCase() ? deviceParam : device}
@@ -159,8 +185,8 @@ class MobilePushPreviewV2 extends React.Component { // eslint-disable-line react
159
185
  hasAndroid = get(templateData, 'versions.base.ANDROID') || get(templateData, 'androidContent.title');
160
186
  hasIos = get(templateData, 'versions.base.IOS') || get(templateData, 'iosContent.title');
161
187
  } else if (this.props.channel === INAPP.toUpperCase()) {
162
- hasAndroid = get(templateData, 'versions.base.content.ANDROID') || get(templateData, 'androidContent.title');
163
- hasIos = get(templateData, 'versions.base.content.IOS') || get(templateData, 'iosContent.title');
188
+ hasAndroid = get(templateData, 'versions.base.content.ANDROID') || get(templateData, 'androidContent');
189
+ hasIos = get(templateData, 'versions.base.content.IOS') || get(templateData, 'iosContent');
164
190
  }
165
191
  const hasBothAndroidAndIos = hasAndroid && hasIos;
166
192
  return (
@@ -1,5 +1,16 @@
1
1
  @import '~@capillarytech/cap-ui-library/styles/_variables.scss';
2
2
 
3
+ .shell-v2 {
4
+ &.align-center {
5
+ .preview-image-container {
6
+ position: relative;
7
+ display: inline-block;
8
+ width: 100%;
9
+ height: 100%;
10
+ }
11
+ }
12
+ }
13
+
3
14
  .shell-v2 {
4
15
  position: relative;
5
16
  -webkit-transform: translate(-50%);
@@ -16,10 +27,11 @@
16
27
  &.mobilepush
17
28
 
18
29
  .inapp-message-container-POPUP-ANDROID {
30
+ background-color: $CAP_WHITE;
19
31
  position: absolute;
20
- top: 20%;
32
+ top: 30%;
21
33
  display: flex;
22
- width: 200px;
34
+ width: 14.286rem;
23
35
  left: 28%;
24
36
 
25
37
  .inapp-title-POPUP-ANDROID {
@@ -59,10 +71,10 @@
59
71
  .inapp-message-container-HEADER-ANDROID {
60
72
  position: absolute;
61
73
  display: flex;
62
- // bottom: 19.4rem;
63
- width: 200px;
64
- left: 28%;
65
- top: 20px;
74
+ width: 17.571rem;
75
+ left: 18%;
76
+ top: 4.071rem;
77
+ background-color: $CAP_WHITE;
66
78
 
67
79
  .inapp-title-HEADER-ANDROID {
68
80
  left: 34%;
@@ -129,22 +141,24 @@
129
141
 
130
142
 
131
143
  .inapp-message-container-FOOTER-ANDROID {
144
+ background-color: $CAP_WHITE;
132
145
  position: absolute;
133
146
  display: flex;
134
147
  justify-content: center;
135
- bottom: 1.5%;
136
- width: 200px;
137
- left: 28%;
148
+ bottom: 2.5%;
149
+ width: 16.571rem;
150
+ left: 21%;
151
+ background-color: $CAP_WHITE;
138
152
 
139
153
  .inapp-title-FOOTER-ANDROID {
140
- width: 90px;
154
+ width: 6.429rem;
141
155
  height: 12px;
142
156
  text-align: left;
143
157
  left: 34%;
144
158
  justify-content: center;
145
159
  position: relative;
146
160
  right: 33%;
147
- bottom: -22px;
161
+ bottom: -1.429rem;
148
162
  font-size: 10px;
149
163
  text-overflow: ellipsis;
150
164
  overflow: hidden;
@@ -201,10 +215,12 @@
201
215
  }
202
216
 
203
217
  .inapp-message-container-FULLSCREEN-ANDROID {
218
+ background-color: $CAP_WHITE;
204
219
  position: absolute;
205
220
  top: 10%;
206
221
  display: flex;
207
- left: 29%;
222
+ left: 20%;
223
+ width: 16.643rem;
208
224
  .inapp-title-FULLSCREEN-ANDROID {
209
225
  text-overflow: ellipsis;
210
226
  overflow: hidden;
@@ -238,10 +254,11 @@
238
254
  }
239
255
 
240
256
  .inapp-message-container-POPUP-iOS {
257
+ background-color: $CAP_WHITE;
241
258
  position: absolute;
242
- top: 20%;
259
+ top: 30%;
243
260
  display: flex;
244
- width: 200px;
261
+ width: 14.286rem;
245
262
  left: 28%;
246
263
 
247
264
  .inapp-title-POPUP-iOS {
@@ -278,18 +295,19 @@
278
295
  }
279
296
 
280
297
  .inapp-message-container-HEADER-iOS {
298
+ background-color: $CAP_WHITE;
281
299
  position: absolute;
282
300
  display: flex;
283
- width: 200px;
284
- left: 28%;
285
- top: 3%;
301
+ width: 16.571rem;
302
+ left: 20%;
303
+ top: 10%;
286
304
 
287
305
  .inapp-title-HEADER-iOS {
288
306
  left: 34%;
289
307
  top: 15%;
290
308
  position: relative;
291
309
  font-size: 10px;
292
- width: 90px;
310
+ width: 6.429rem;
293
311
  height: 12px;
294
312
  text-overflow: ellipsis;
295
313
  overflow: hidden;
@@ -300,7 +318,7 @@
300
318
 
301
319
  .without-image-title-HEADER-ios {
302
320
  left: 26%;
303
- top: 18px;
321
+ top: 1.286rem;
304
322
  text-align: center;
305
323
  }
306
324
 
@@ -330,7 +348,7 @@
330
348
  .without-image-content-HEADER-ios {
331
349
  width: 92%;
332
350
  left: 6%;
333
- top: 21px;
351
+ top: 1.5rem;
334
352
  }
335
353
 
336
354
  .inapp-button-HEADER-iOS {
@@ -347,12 +365,14 @@
347
365
  }
348
366
 
349
367
  .inapp-message-container-FOOTER-iOS {
368
+ background-color: $CAP_WHITE;
350
369
  position: absolute;
351
370
  display: flex;
352
371
  justify-content: center;
353
- width: 200px;
354
- left: 28%;
355
- top: 75%;
372
+ width: 16.571rem;
373
+ left: 20%;
374
+ top: 42%;
375
+ bottom: 3.5%;
356
376
 
357
377
  .inapp-title-FOOTER-iOS {
358
378
  width: 90px;
@@ -420,10 +440,12 @@
420
440
  }
421
441
 
422
442
  .inapp-message-container-FULLSCREEN-iOS {
443
+ background-color: $CAP_WHITE;
423
444
  position: absolute;
424
445
  top: 10%;
425
446
  display: flex;
426
- left: 29%;
447
+ left: 20%;
448
+ width: 16.643rem;
427
449
  .inapp-title-FULLSCREEN-iOS {
428
450
  text-overflow: ellipsis;
429
451
  overflow: hidden;
@@ -455,6 +477,15 @@
455
477
  bottom: -1%;
456
478
  }
457
479
  }
480
+
481
+ .inapp-message-container-MODAL-ANDROID, .inapp-message-container-MODAL-iOS {
482
+ background-color: $CAP_WHITE;
483
+ position: absolute;
484
+ top: 30%;
485
+ display: flex;
486
+ width: 14.286rem;
487
+ left: 28%;
488
+ }
458
489
  }
459
490
 
460
491
 
@@ -37,14 +37,8 @@ import {
37
37
  import { VIBER, FACEBOOK } from '../../v2Containers/App/constants';
38
38
  import Carousel from '../Carousel';
39
39
  import whatsappMobileAndroid from './assets/images/whatsapp_mobile_android.svg';
40
- import inAppMobileAndroidModal from './assets/images/inapp_mobile_android_modal.svg';
41
- import inAppMobileAndroidTop from './assets/images/inapp_mobile_android_top.svg';
42
- import inAppMobileAndroidBottom from './assets/images/inapp_mobile_android_bottom.svg';
43
- import inAppMobileAndroidFull from './assets/images/inapp_mobile_android_full.svg';
44
- import inAppMobileIOSModal from './assets/images/inapp_mobile_ios_modal.svg';
45
- import inAppMobileIOSTop from './assets/images/inapp_mobile_ios_top.svg';
46
- import inAppMobileIOSBottom from './assets/images/inapp_mobile_ios_bottom.svg';
47
- import inAppMobileIOSFull from './assets/images/inapp_mobile_ios_full.svg';
40
+ import inAppMobileDeviceAndroid from '../../assets/Android.png';
41
+ import inAppMobileDeviceIos from '../../assets/iOS.png';
48
42
  import whatsappImageEmptyPreview from './assets/images/empty_image_preview.svg';
49
43
  import whatsappVideoEmptyPreview from './assets/images/empty_video_preview.svg';
50
44
  import videoPlay from '../../assets/videoPlay.svg';
@@ -239,7 +233,7 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
239
233
  } = this.props;
240
234
  let content = channel && channel.toLowerCase() === 'sms' ? [this.props.content] : this.props.content;
241
235
  const { formatMessage } = intl;
242
- const { rcsPreviewContent, inAppPreviewContent, viberPreviewContent } = content || {};
236
+ const { rcsPreviewContent, inAppPreviewContent, viberPreviewContent, isBeeFreeTemplate } = content || {};
243
237
  const { rcsImageSrc, rcsVideoSrc, rcsTitle, rcsDesc, rcsSuggestions } = rcsPreviewContent || {};
244
238
  const {
245
239
  videoParams,
@@ -247,7 +241,11 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
247
241
  buttonText: viberButtonText,
248
242
  messageContent: viberMessageContent,
249
243
  } = viberPreviewContent || {};
244
+ let isHTMLContent = true;
250
245
  const {mediaPreview = {}, templateTitle = "", templateMsg = "", ctaData} = inAppPreviewContent || {};
246
+ if(templateTitle !== undefined && ctaData !== undefined && templateMsg !== undefined) {
247
+ isHTMLContent = false;
248
+ }
251
249
  let smsDetails = {};
252
250
  // let smsText = '';
253
251
  if (this.props.content && this.props.charCounterEnabled) {
@@ -464,27 +462,9 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
464
462
 
465
463
  const getPreviewImage = () => {
466
464
  if (this.props.device === ANDROID) {
467
- switch (templateLayoutType) {
468
- case INAPP_MESSAGE_LAYOUT_TYPES.MODAL:
469
- return inAppMobileAndroidModal;
470
- case INAPP_MESSAGE_LAYOUT_TYPES.TOPBANNER:
471
- return inAppMobileAndroidTop;
472
- case INAPP_MESSAGE_LAYOUT_TYPES.BOTTOMBANNER:
473
- return inAppMobileAndroidBottom;
474
- default:
475
- return inAppMobileAndroidFull;
476
- }
465
+ return inAppMobileDeviceAndroid;
477
466
  } else {
478
- switch (templateLayoutType) {
479
- case INAPP_MESSAGE_LAYOUT_TYPES.MODAL:
480
- return inAppMobileIOSModal;
481
- case INAPP_MESSAGE_LAYOUT_TYPES.TOPBANNER:
482
- return inAppMobileIOSTop;
483
- case INAPP_MESSAGE_LAYOUT_TYPES.BOTTOMBANNER:
484
- return inAppMobileIOSBottom;
485
- default:
486
- return inAppMobileIOSFull;
487
- }
467
+ return inAppMobileDeviceIos;
488
468
  }
489
469
  };
490
470
 
@@ -1374,7 +1354,34 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
1374
1354
  </div>
1375
1355
  )}
1376
1356
  {channel?.toUpperCase() === INAPP && (
1377
- <div className="shell-v2 align-center">
1357
+ isBeeFreeTemplate ? (
1358
+ <div className="shell-v2 align-center">
1359
+ <CapRow className="preview-image-container">
1360
+ <CapImage
1361
+ className="preview-image"
1362
+ src={this.props.device === ANDROID ? inAppMobileDeviceAndroid : inAppMobileDeviceIos}
1363
+ alt={formatMessage(messages.previewGenerated)}
1364
+ />
1365
+ <iframe
1366
+ srcDoc={inAppPreviewContent?.value}
1367
+ title={formatMessage(messages.inappPreview)}
1368
+ style={{
1369
+ position: 'absolute',
1370
+ top: '3rem',
1371
+ left: '5rem',
1372
+ width: '60%',
1373
+ height: '89%',
1374
+ zIndex: 1,
1375
+ pointerEvents: 'none',
1376
+ backgroundColor: 'white',
1377
+ borderRadius: `${CAP_SPACE_08}`,
1378
+ }}
1379
+ frameBorder="0"
1380
+ />
1381
+ </CapRow>
1382
+ </div>
1383
+ ): (
1384
+ <div className="shell-v2 align-center">
1378
1385
  <CapImage
1379
1386
  className="preview-image"
1380
1387
  src={getPreviewImage()}
@@ -1382,9 +1389,15 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
1382
1389
  />
1383
1390
  <div className="preview-image">
1384
1391
  <div
1385
- className={`inapp-message-container-${templateLayoutType}-${device} sms`}
1392
+ className={`inapp-message-container-${templateLayoutType}-${device} inapp`}
1386
1393
  >
1387
- <div className="preview-inapp-screen">
1394
+ {isHTMLContent ? (
1395
+ <div
1396
+ className="inapp-html-content"
1397
+ dangerouslySetInnerHTML={{ __html: templateMsg }}
1398
+ />
1399
+ ) : (
1400
+ <div className="preview-inapp-screen">
1388
1401
  {
1389
1402
  <CapLabel
1390
1403
  type="label15"
@@ -1449,9 +1462,11 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
1449
1462
  </CapButton>
1450
1463
  )}
1451
1464
  </div>
1465
+ )}
1452
1466
  </div>
1453
1467
  </div>
1454
1468
  </div>
1469
+ )
1455
1470
  )}
1456
1471
  </CapRow>
1457
1472
  </CapColumn>
@@ -94,4 +94,8 @@ export default defineMessages({
94
94
  id: 'creatives.componentsV2.TemplatePreview.videoNotSupported',
95
95
  defaultMessage: 'Your browser does not support the video tag.',
96
96
  },
97
+ inappPreview: {
98
+ id: 'creatives.componentsV2.TemplatePreview.inappPreview',
99
+ defaultMessage: 'Inapp Preview',
100
+ },
97
101
  });
@@ -66,6 +66,7 @@
66
66
  flex: 1;
67
67
  background-color: #f0f2f5;
68
68
  padding: $CAP_SPACE_08 $CAP_SPACE_24;
69
+ overflow-y: auto;
69
70
  }
70
71
 
71
72
  .panel-section {