@capillarytech/creatives-library 8.0.246-alpha.0 → 8.0.246

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 (133) hide show
  1. package/assets/Android.png +0 -0
  2. package/assets/iOS.png +0 -0
  3. package/constants/unified.js +1 -2
  4. package/initialReducer.js +0 -2
  5. package/package.json +1 -1
  6. package/services/api.js +0 -10
  7. package/services/tests/api.test.js +0 -18
  8. package/utils/common.js +0 -5
  9. package/utils/commonUtils.js +5 -28
  10. package/utils/tests/commonUtil.test.js +0 -224
  11. package/utils/transformTemplateConfig.js +10 -0
  12. package/v2Components/CapDeviceContent/index.js +56 -61
  13. package/v2Components/CapTagList/index.js +1 -6
  14. package/v2Components/CapTagListWithInput/index.js +1 -5
  15. package/v2Components/CapTagListWithInput/messages.js +1 -1
  16. package/v2Components/CapWhatsappCTA/tests/index.test.js +0 -5
  17. package/v2Components/ErrorInfoNote/index.js +72 -447
  18. package/v2Components/ErrorInfoNote/messages.js +0 -22
  19. package/v2Components/ErrorInfoNote/style.scss +4 -280
  20. package/v2Components/FormBuilder/tests/index.test.js +4 -13
  21. package/v2Components/HtmlEditor/HTMLEditor.js +94 -642
  22. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +133 -1135
  23. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +16 -27
  24. package/v2Components/HtmlEditor/_htmlEditor.scss +45 -108
  25. package/v2Components/HtmlEditor/_index.lazy.scss +1 -1
  26. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +101 -13
  27. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +139 -148
  28. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +1 -2
  29. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
  30. package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +0 -9
  31. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +1 -1
  32. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +0 -22
  33. package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +7 -4
  34. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +45 -35
  35. package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +3 -1
  36. package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
  37. package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +6 -7
  38. package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +6 -3
  39. package/v2Components/HtmlEditor/components/PreviewPane/index.js +13 -11
  40. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  41. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +152 -0
  42. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +31 -49
  43. package/v2Components/HtmlEditor/constants.js +20 -29
  44. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +16 -373
  45. package/v2Components/HtmlEditor/hooks/useEditorContent.js +2 -5
  46. package/v2Components/HtmlEditor/hooks/useInAppContent.js +146 -88
  47. package/v2Components/HtmlEditor/hooks/useValidation.js +45 -150
  48. package/v2Components/HtmlEditor/index.js +1 -1
  49. package/v2Components/HtmlEditor/messages.js +85 -95
  50. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +102 -134
  51. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +25 -23
  52. package/v2Components/HtmlEditor/utils/validationAdapter.js +41 -66
  53. package/v2Components/MobilePushPreviewV2/index.js +7 -32
  54. package/v2Components/TemplatePreview/_templatePreview.scss +24 -44
  55. package/v2Components/TemplatePreview/index.js +32 -47
  56. package/v2Components/TemplatePreview/messages.js +0 -4
  57. package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +0 -1
  58. package/v2Components/TestAndPreviewSlidebox/index.js +25 -31
  59. package/v2Containers/BeeEditor/index.js +90 -172
  60. package/v2Containers/CreativesContainer/SlideBoxContent.js +51 -128
  61. package/v2Containers/CreativesContainer/SlideBoxFooter.js +12 -113
  62. package/v2Containers/CreativesContainer/SlideBoxHeader.js +1 -2
  63. package/v2Containers/CreativesContainer/constants.js +0 -1
  64. package/v2Containers/CreativesContainer/index.js +46 -238
  65. package/v2Containers/CreativesContainer/messages.js +0 -8
  66. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +2 -11
  67. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +50 -38
  68. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +0 -91
  69. package/v2Containers/Email/actions.js +0 -7
  70. package/v2Containers/Email/constants.js +1 -5
  71. package/v2Containers/Email/index.js +30 -229
  72. package/v2Containers/Email/messages.js +0 -32
  73. package/v2Containers/Email/reducer.js +1 -12
  74. package/v2Containers/Email/sagas.js +7 -61
  75. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +0 -2
  76. package/v2Containers/Email/tests/sagas.test.js +1 -1
  77. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +15 -210
  78. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +74 -40
  79. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +67 -2
  80. package/v2Containers/EmailWrapper/constants.js +0 -2
  81. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +77 -629
  82. package/v2Containers/EmailWrapper/index.js +23 -103
  83. package/v2Containers/EmailWrapper/messages.js +1 -61
  84. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +214 -0
  85. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +77 -509
  86. package/v2Containers/InApp/actions.js +0 -7
  87. package/v2Containers/InApp/constants.js +4 -20
  88. package/v2Containers/InApp/index.js +357 -801
  89. package/v2Containers/InApp/index.scss +3 -4
  90. package/v2Containers/InApp/messages.js +3 -7
  91. package/v2Containers/InApp/reducer.js +3 -21
  92. package/v2Containers/InApp/sagas.js +9 -29
  93. package/v2Containers/InApp/selectors.js +5 -25
  94. package/v2Containers/InApp/tests/index.test.js +50 -154
  95. package/v2Containers/InApp/tests/reducer.test.js +0 -34
  96. package/v2Containers/InApp/tests/sagas.test.js +9 -61
  97. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +0 -3
  98. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +0 -2
  99. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +0 -2
  100. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +0 -9
  101. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +0 -12
  102. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +0 -4
  103. package/v2Containers/TagList/index.js +19 -62
  104. package/v2Containers/Templates/_templates.scss +1 -60
  105. package/v2Containers/Templates/index.js +4 -89
  106. package/v2Containers/Templates/messages.js +0 -4
  107. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +0 -35
  108. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +0 -874
  109. package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +0 -254
  110. package/v2Components/HtmlEditor/components/ValidationTabs/index.js +0 -363
  111. package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +0 -51
  112. package/v2Components/HtmlEditor/hooks/__tests__/useValidation.apiErrors.test.js +0 -630
  113. package/v2Containers/BeePopupEditor/constants.js +0 -10
  114. package/v2Containers/BeePopupEditor/index.js +0 -193
  115. package/v2Containers/BeePopupEditor/tests/index.test.js +0 -627
  116. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +0 -1317
  117. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +0 -1605
  118. package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +0 -520
  119. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +0 -643
  120. package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +0 -376
  121. package/v2Containers/InApp/__tests__/sagas.test.js +0 -363
  122. package/v2Containers/InApp/tests/selectors.test.js +0 -612
  123. package/v2Containers/InAppWrapper/components/InAppWrapperView.js +0 -162
  124. package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +0 -267
  125. package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +0 -9
  126. package/v2Containers/InAppWrapper/constants.js +0 -16
  127. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +0 -473
  128. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +0 -198
  129. package/v2Containers/InAppWrapper/index.js +0 -148
  130. package/v2Containers/InAppWrapper/messages.js +0 -49
  131. package/v2Containers/InappAdvance/index.js +0 -1099
  132. package/v2Containers/InappAdvance/index.scss +0 -10
  133. package/v2Containers/InappAdvance/tests/index.test.js +0 -448
@@ -48,14 +48,13 @@ export const CapTagListWithInput = (props) => {
48
48
  showTagList = true,
49
49
  showInput = true,
50
50
  inputProps = {},
51
- popoverPlacement,
52
51
  } = props;
53
52
 
54
53
  const { formatMessage } = intl;
55
54
 
56
55
  return (
57
56
  <CapColumn style={containerStyle}>
58
- <CapRow align="middle" type="flex">
57
+ <CapRow style={{display: 'flex', flexDirection: 'row'}}>
59
58
  {showHeading && headingText && (
60
59
  <CapHeading type={headingType} style={headingStyle}>
61
60
  {headingText}
@@ -77,7 +76,6 @@ export const CapTagListWithInput = (props) => {
77
76
  selectedOfferDetails={selectedOfferDetails}
78
77
  eventContextTags={eventContextTags}
79
78
  style={tagListStyle}
80
- popoverPlacement={popoverPlacement}
81
79
  />
82
80
  )}
83
81
  </CapRow>
@@ -138,7 +136,6 @@ CapTagListWithInput.propTypes = {
138
136
  showHeading: PropTypes.bool,
139
137
  showTagList: PropTypes.bool,
140
138
  showInput: PropTypes.bool,
141
- popoverPlacement: PropTypes.string,
142
139
  };
143
140
 
144
141
  CapTagListWithInput.defaultProps = {
@@ -167,7 +164,6 @@ CapTagListWithInput.defaultProps = {
167
164
  showTagList: true,
168
165
  showInput: true,
169
166
  inputProps: {},
170
- popoverPlacement: undefined,
171
167
  };
172
168
 
173
169
  export default injectIntl(CapTagListWithInput);
@@ -5,6 +5,6 @@ const prefix = 'creatives.componentsV2.CapTagListWithInput';
5
5
  export default defineMessages({
6
6
  addLabels: {
7
7
  id: `${prefix}.addLabels`,
8
- defaultMessage: 'Add label',
8
+ defaultMessage: 'Add labels',
9
9
  },
10
10
  });
@@ -4,11 +4,6 @@ import '@testing-library/jest-dom';
4
4
  import { render, screen, fireEvent } from '../../../utils/test-utils';
5
5
  import { CapWhatsappCTA } from '../index';
6
6
 
7
- // Mock the missing reducer
8
- jest.mock('@capillarytech/cap-ui-library/CapCollapsibleLeftNavigation/reducer', () => {
9
- return (state = {}) => state;
10
- }, { virtual: true });
11
-
12
7
  const updateHandler = jest.fn();
13
8
  const deleteHandler = jest.fn();
14
9
  const initializeComponent = (
@@ -1,435 +1,21 @@
1
- import React, { useState, useMemo } from 'react';
2
- import PropTypes from 'prop-types';
3
- import CapRow from '@capillarytech/cap-ui-library/CapRow';
4
- import CapButton from '@capillarytech/cap-ui-library/CapButton';
5
- import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
6
- import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
7
- import CapTab from '@capillarytech/cap-ui-library/CapTab';
8
- import CapTooltip from '@capillarytech/cap-ui-library/CapTooltip';
1
+ import React from "react";
2
+ import PropTypes from "prop-types";
3
+ import CapRow from "@capillarytech/cap-ui-library/CapRow";
4
+ import CapButton from "@capillarytech/cap-ui-library/CapButton";
5
+ import CapIcon from "@capillarytech/cap-ui-library/CapIcon";
6
+ import CapLabel from "@capillarytech/cap-ui-library/CapLabel";
7
+ import CapList from "@capillarytech/cap-ui-library/CapList";
9
8
  import {
10
9
  FormattedMessage,
10
+ FormattedNumber,
11
11
  injectIntl,
12
- } from 'react-intl';
13
- import './style.scss';
14
- import messages from './messages';
15
- import { processErrors } from './utils';
16
- import ErrorTypeRenderer from './ErrorTypeRenderer';
17
- import { ANDROID, GENERIC, IOS } from '../../v2Containers/CreativesContainer/constants';
12
+ } from "react-intl";
13
+ import "./style.scss";
14
+ import messages from "./messages";
15
+ import { processErrors } from "./utils";
16
+ import ErrorTypeRenderer from "./ErrorTypeRenderer";
17
+ import { ANDROID, GENERIC, IOS } from "../../v2Containers/CreativesContainer/constants";
18
18
 
19
- // Label issue patterns - syntax errors related to tags
20
- const LABEL_ISSUE_PATTERNS = [
21
- 'tag must be paired',
22
- 'open tag match failed',
23
- 'closed tag match failed',
24
- 'unclosed',
25
- 'missing required',
26
- 'tag-pair',
27
- 'attr-value-not-empty',
28
- 'attr-no-duplication',
29
- 'tag-self-close',
30
- 'spec-char-escape',
31
- '</html>',
32
- '<html>',
33
- '</div>',
34
- '<div>',
35
- '{{',
36
- '}}',
37
- ];
38
-
39
- /**
40
- * Categorize error messages into HTML, Label, and Liquid categories
41
- */
42
- const categorizeErrorMessages = (standardErrors, liquidErrors) => {
43
- const htmlIssues = [];
44
- const labelIssues = [];
45
- const liquidIssues = [];
46
-
47
- // Process standard errors
48
- (standardErrors || []).forEach((error, index) => {
49
- const errorLower = (error || '').toLowerCase();
50
-
51
- // Check if it's a Label (tag syntax) issue
52
- const isLabelIssue = LABEL_ISSUE_PATTERNS.some(
53
- (pattern) => errorLower.includes(pattern.toLowerCase()),
54
- );
55
-
56
- if (isLabelIssue) {
57
- labelIssues.push({
58
- message: error,
59
- severity: 'error',
60
- index,
61
- source: 'label',
62
- });
63
- } else {
64
- htmlIssues.push({
65
- message: error,
66
- severity: 'error',
67
- index,
68
- source: 'html',
69
- });
70
- }
71
- });
72
-
73
- // Process liquid errors
74
- (liquidErrors || []).forEach((error, index) => {
75
- liquidIssues.push({
76
- message: error,
77
- severity: 'error',
78
- index,
79
- source: 'liquid',
80
- });
81
- });
82
-
83
- return { htmlIssues, labelIssues, liquidIssues };
84
- };
85
-
86
- /**
87
- * Get icon based on severity
88
- */
89
- const getSeverityIcon = (severity) => {
90
- if (severity === 'warning') {
91
- return <CapIcon type="alert-warning" className="error-info-note__icon error-info-note__icon--warning" />;
92
- }
93
- return <CapIcon type="warning-circle" className="error-info-note__icon error-info-note__icon--error" />;
94
- };
95
-
96
- /**
97
- * Tab content component
98
- */
99
- const TabContent = ({ issues, onErrorClick, intl }) => {
100
- if (!issues || issues.length === 0) {
101
- return null;
102
- }
103
-
104
- const handleNavigateClick = (issue, e) => {
105
- e.stopPropagation();
106
- if (onErrorClick) {
107
- onErrorClick(issue);
108
- }
109
- };
110
-
111
- return (
112
- <div className="error-info-note__content">
113
- {issues.map((issue, index) => {
114
- const { severity, message } = issue;
115
- const key = `${message}-${index}`;
116
-
117
- // Parse line and char from message if present (format: "... Line X, Char Y.")
118
- const lineMatch = message.match(/Line\s+(\d+)/i);
119
- const charMatch = message.match(/Char\s+(\d+)/i);
120
- const line = lineMatch ? parseInt(lineMatch[1], 10) : null;
121
- const char = charMatch ? parseInt(charMatch[1], 10) : null;
122
-
123
- // Extract rule from message (format: "... • rule-name")
124
- const ruleMatch = message.match(/•\s*([a-z-]+)$/i);
125
- const rule = ruleMatch ? ruleMatch[1] : null;
126
-
127
- // Clean message (remove line/char and rule parts for display)
128
- let displayMessage = message;
129
- if (lineMatch) {
130
- displayMessage = displayMessage.replace(/Line\s+\d+,?\s*/gi, '');
131
- }
132
- if (charMatch) {
133
- displayMessage = displayMessage.replace(/Char\s+\d+\.?\s*/gi, '');
134
- }
135
- if (ruleMatch) {
136
- displayMessage = displayMessage.replace(/•\s*[a-z-]+$/i, '');
137
- }
138
- displayMessage = displayMessage.trim();
139
-
140
- return (
141
- <div
142
- key={key}
143
- className={`error-info-note__item error-info-note__item--${severity || 'error'}`}
144
- >
145
- <div className="error-info-note__item-icon">
146
- {getSeverityIcon(severity)}
147
- </div>
148
- <div className="error-info-note__item-content">
149
- <span className="error-info-note__item-message">
150
- {displayMessage}
151
- </span>
152
- {line && (
153
- <span className="error-info-note__item-location">
154
- Line
155
- {' '}
156
- {line}
157
- {char ? `, Char ${char}` : ''}
158
- .
159
- </span>
160
- )}
161
- {rule && (
162
- <span className="error-info-note__item-rule">
163
-
164
- {' '}
165
- {rule}
166
- </span>
167
- )}
168
- </div>
169
- {onErrorClick && (
170
- <CapTooltip title={intl?.formatMessage ? intl.formatMessage({ id: 'errorInfoNote.navigateToError', defaultMessage: 'Go to error location' }) : 'Go to error location'}>
171
- <button
172
- type="button"
173
- className="error-info-note__item-navigate"
174
- onClick={(e) => handleNavigateClick({ ...issue, line, column: char }, e)}
175
- aria-label="Go to error location"
176
- >
177
- <CapIcon type="redirection" />
178
- </button>
179
- </CapTooltip>
180
- )}
181
- </div>
182
- );
183
- })}
184
- </div>
185
- );
186
- };
187
-
188
- TabContent.propTypes = {
189
- issues: PropTypes.array,
190
- onErrorClick: PropTypes.func,
191
- intl: PropTypes.object,
192
- };
193
-
194
- TabContent.defaultProps = {
195
- issues: [],
196
- onErrorClick: null,
197
- intl: null,
198
- };
199
-
200
- /**
201
- * ErrorInfoNote Component with Tabbed Interface
202
- * @param {boolean} useLegacyDisplay - If true, uses simple list display instead of tabbed interface (for BEE Editor)
203
- */
204
- export const ErrorInfoNote = (props) => {
205
- const {
206
- errorMessages,
207
- onErrorClick,
208
- onClose,
209
- isLiquidEnabled = true,
210
- intl,
211
- useLegacyDisplay = false, // Use simple list display instead of tabs (for BEE Editor)
212
- } = props;
213
-
214
- const [isDismissed, setIsDismissed] = useState(false);
215
- const [activeKey, setActiveKey] = useState(null);
216
-
217
- const {
218
- LIQUID_ERROR_MSG: rawLiquidErrors = [],
219
- STANDARD_ERROR_MSG: rawStandardErrors = [],
220
- } = errorMessages || {};
221
-
222
- // Detect if platform-specific (ANDROID/IOS) or GENERIC
223
- const isObject = typeof rawStandardErrors === 'object' && rawStandardErrors !== null;
224
- const isNotArray = !Array.isArray(rawLiquidErrors);
225
- const hasPlatformKeys = isObject && isNotArray && [ANDROID, IOS, GENERIC].some((key) => key in rawLiquidErrors);
226
-
227
- // For platform-specific errors or when useLegacyDisplay is true, use the legacy renderer
228
- if (hasPlatformKeys) {
229
- const androidErrors = {
230
- STANDARD: processErrors(rawStandardErrors, 'standard', ANDROID, messages),
231
- LIQUID: processErrors(rawLiquidErrors, 'liquid', ANDROID, messages),
232
- };
233
- const iosErrors = {
234
- STANDARD: processErrors(rawStandardErrors, 'standard', IOS, messages),
235
- LIQUID: processErrors(rawLiquidErrors, 'liquid', IOS, messages),
236
- };
237
- return (
238
- <ErrorTypeRenderer
239
- genericErrors={null}
240
- androidErrors={androidErrors}
241
- iosErrors={iosErrors}
242
- ErrorSectionComponent={ErrorSection}
243
- />
244
- );
245
- }
246
-
247
- // For BEE Editor (useLegacyDisplay=true), use simple list display without tabs
248
- if (useLegacyDisplay) {
249
- const standardErrors = Array.isArray(rawStandardErrors) ? rawStandardErrors : [];
250
- const liquidErrors = Array.isArray(rawLiquidErrors) ? rawLiquidErrors : [];
251
- const hasStandardErrors = standardErrors.length > 0;
252
- const hasLiquidErrors = liquidErrors.length > 0 && isLiquidEnabled;
253
-
254
- if (!hasStandardErrors && !hasLiquidErrors) {
255
- return null;
256
- }
257
-
258
- return (
259
- <div className="error-container error-container--legacy">
260
- {hasStandardErrors && (
261
- <ErrorSection
262
- title={<FormattedMessage {...messages.standardErrorHeader} />}
263
- errors={standardErrors}
264
- liquidError={false}
265
- />
266
- )}
267
- {hasLiquidErrors && (
268
- <ErrorSection
269
- title={<FormattedMessage {...messages.dynamicErrorHeader} />}
270
- errors={liquidErrors}
271
- liquidError
272
- />
273
- )}
274
- </div>
275
- );
276
- }
277
-
278
- // Categorize errors for tabbed interface
279
- const { htmlIssues, labelIssues, liquidIssues } = useMemo(() => categorizeErrorMessages(
280
- Array.isArray(rawStandardErrors) ? rawStandardErrors : [],
281
- Array.isArray(rawLiquidErrors) ? rawLiquidErrors : [],
282
- ), [rawStandardErrors, rawLiquidErrors]);
283
-
284
- // Calculate counts
285
- const htmlCount = htmlIssues.length;
286
- const labelCount = labelIssues.length;
287
- const liquidCount = liquidIssues.length;
288
- const totalCount = htmlCount + labelCount + (isLiquidEnabled ? liquidCount : 0);
289
-
290
- // Set default active key
291
- useMemo(() => {
292
- if (!activeKey) {
293
- if (htmlCount > 0) {
294
- setActiveKey('html');
295
- } else if (labelCount > 0) {
296
- setActiveKey('label');
297
- } else if (liquidCount > 0 && isLiquidEnabled) {
298
- setActiveKey('liquid');
299
- }
300
- }
301
- }, [htmlCount, labelCount, liquidCount, isLiquidEnabled, activeKey]);
302
-
303
- // Handle close
304
- const handleClose = () => {
305
- setIsDismissed(true);
306
- if (onClose) {
307
- onClose();
308
- }
309
- };
310
-
311
- // Handle liquid documentation click
312
- const handleLiquidDocClick = () => {
313
- window.open(
314
- 'https://docs.capillarytech.com/docs/liquid-language-in-messages',
315
- '_blank',
316
- );
317
- };
318
-
319
- // Don't render if no issues or dismissed
320
- if (totalCount === 0 || isDismissed) {
321
- return null;
322
- }
323
-
324
- // Build tab panes (CapTab uses 'panes' with 'tab' and 'content' properties)
325
- const tabPanes = [];
326
-
327
- if (htmlCount > 0) {
328
- tabPanes.push({
329
- key: 'html',
330
- tab: (
331
- <span className="error-info-note__tab-label">
332
- <FormattedMessage {...messages.htmlIssues} />
333
- <span className="error-info-note__tab-count">
334
- (
335
- {htmlCount}
336
- )
337
- </span>
338
- </span>
339
- ),
340
- content: (
341
- <TabContent
342
- issues={htmlIssues}
343
- onErrorClick={onErrorClick}
344
- intl={intl}
345
- />
346
- ),
347
- });
348
- }
349
-
350
- if (labelCount > 0) {
351
- tabPanes.push({
352
- key: 'label',
353
- tab: (
354
- <span className="error-info-note__tab-label">
355
- <FormattedMessage {...messages.labelIssues} />
356
- <span className="error-info-note__tab-count">
357
- (
358
- {labelCount}
359
- )
360
- </span>
361
- </span>
362
- ),
363
- content: (
364
- <TabContent
365
- issues={labelIssues}
366
- onErrorClick={onErrorClick}
367
- intl={intl}
368
- />
369
- ),
370
- });
371
- }
372
-
373
- if (isLiquidEnabled && liquidCount > 0) {
374
- tabPanes.push({
375
- key: 'liquid',
376
- tab: (
377
- <span className="error-info-note__tab-label">
378
- <FormattedMessage {...messages.liquidIssues} />
379
- <span className="error-info-note__tab-count">
380
- (
381
- {liquidCount}
382
- )
383
- </span>
384
- </span>
385
- ),
386
- content: (
387
- <TabContent
388
- issues={liquidIssues}
389
- onErrorClick={onErrorClick}
390
- intl={intl}
391
- />
392
- ),
393
- });
394
- }
395
-
396
- return (
397
- <div className="error-container error-container--tabs">
398
- <CapRow className="error-info-note__header">
399
- <CapTab
400
- activeKey={activeKey || (tabPanes[0]?.key)}
401
- onChange={setActiveKey}
402
- panes={tabPanes}
403
- className="error-info-note__tabs"
404
- />
405
- <CapRow className="error-info-note__actions">
406
- {activeKey === 'liquid' && isLiquidEnabled && (
407
- <CapButton
408
- type="flat"
409
- className="error-info-note__liquid-doc"
410
- onClick={handleLiquidDocClick}
411
- >
412
- <FormattedMessage {...messages.liquidDoc} />
413
- <CapIcon size="s" type="launch" />
414
- </CapButton>
415
- )}
416
- <CapTooltip title={intl?.formatMessage ? intl.formatMessage({ id: 'errorInfoNote.closePanel', defaultMessage: 'Close validation panel' }) : 'Close validation panel'}>
417
- <button
418
- type="button"
419
- className="error-info-note__close"
420
- onClick={handleClose}
421
- aria-label="Close validation panel"
422
- >
423
- <CapIcon type="close" />
424
- </button>
425
- </CapTooltip>
426
- </CapRow>
427
- </CapRow>
428
- </div>
429
- );
430
- };
431
-
432
- // Legacy ErrorSection component for platform-specific errors (backwards compatibility)
433
19
  const ErrorSection = ({
434
20
  title,
435
21
  errors,
@@ -440,7 +26,7 @@ const ErrorSection = ({
440
26
  {title && (
441
27
  <CapRow
442
28
  className={`error-header ${
443
- !liquidError ? 'standard-error-header' : ''
29
+ !liquidError ? "standard-error-header" : ""
444
30
  }`}
445
31
  >
446
32
  <>
@@ -452,7 +38,8 @@ const ErrorSection = ({
452
38
  <CapButton
453
39
  type="flat"
454
40
  className="add-btn"
455
- onClick={() => window.open('https://docs.capillarytech.com/docs/liquid-language-in-messages', '_blank')}
41
+ onClick={() => window.open("https://docs.capillarytech.com/docs/liquid-language-in-messages", "_blank")
42
+ }
456
43
  >
457
44
  <FormattedMessage {...messages.liquidDoc} />
458
45
  <CapIcon size="s" type="launch" />
@@ -466,15 +53,21 @@ const ErrorSection = ({
466
53
  <CapLabel type="label2">{platformLabel}</CapLabel>
467
54
  </CapRow>
468
55
  )}
469
- <div className="error-list-legacy">
470
- {(errors || []).map((error) => (
471
- <div key={`${error}`} className="error-list-legacy__item">
56
+ <CapList
57
+ className="error-list"
58
+ size="small"
59
+ dataSource={errors}
60
+ renderItem={(error, index) => (
61
+ <CapList.Item>
472
62
  <CapLabel type="label2" className="cap-list-v2-error-item">
63
+ <CapLabel type="label2">
64
+ <FormattedNumber value={index + 1} />.
65
+ </CapLabel>
473
66
  <CapLabel type="label2">{error}</CapLabel>
474
67
  </CapLabel>
475
- </div>
476
- ))}
477
- </div>
68
+ </CapList.Item>
69
+ )}
70
+ />
478
71
  </>
479
72
  );
480
73
 
@@ -491,16 +84,54 @@ ErrorSection.defaultProps = {
491
84
  platformLabel: null,
492
85
  };
493
86
 
87
+ export const ErrorInfoNote = (props) => {
88
+ const { errorMessages } = props;
89
+
90
+ const {
91
+ LIQUID_ERROR_MSG: rawLiquidErrors = [],
92
+ STANDARD_ERROR_MSG: rawStandardErrors = [],
93
+ } = errorMessages || {};
94
+
95
+ // Detect if platform-specific (ANDROID/IOS) or GENERIC
96
+ const isObject = typeof rawStandardErrors === 'object' && rawStandardErrors !== null;
97
+ const isNotArray = !Array.isArray(rawLiquidErrors);
98
+ const hasPlatformKeys = isObject && isNotArray && [ANDROID, IOS, GENERIC].some((key) => key in rawLiquidErrors);
99
+
100
+ if (hasPlatformKeys) {
101
+ // Platform-specific
102
+ const androidErrors = {
103
+ STANDARD: processErrors(rawStandardErrors, 'standard', ANDROID, messages),
104
+ LIQUID: processErrors(rawLiquidErrors, 'liquid', ANDROID, messages),
105
+ };
106
+ const iosErrors = {
107
+ STANDARD: processErrors(rawStandardErrors, 'standard', IOS, messages),
108
+ LIQUID: processErrors(rawLiquidErrors, 'liquid', IOS, messages),
109
+ };
110
+ return (
111
+ <ErrorTypeRenderer
112
+ genericErrors={null}
113
+ androidErrors={androidErrors}
114
+ iosErrors={iosErrors}
115
+ ErrorSectionComponent={ErrorSection}
116
+ />
117
+ );
118
+ }
119
+ // GENERIC (not platform-specific)
120
+ const genericStandard = processErrors(rawStandardErrors, 'standard', null, messages);
121
+ const genericLiquid = processErrors(rawLiquidErrors, 'liquid', null, messages);
122
+ return (
123
+ <ErrorTypeRenderer
124
+ genericErrors={{ standard: genericStandard, liquid: genericLiquid }}
125
+ ErrorSectionComponent={ErrorSection}
126
+ />
127
+ );
128
+ };
129
+
494
130
  ErrorInfoNote.defaultProps = {
495
131
  errorMessages: {
496
132
  LIQUID_ERROR_MSG: [],
497
133
  STANDARD_ERROR_MSG: [],
498
134
  },
499
- onErrorClick: null,
500
- onClose: null,
501
- isLiquidEnabled: true,
502
- intl: null,
503
- useLegacyDisplay: false, // Use simple list display for BEE Editor
504
135
  };
505
136
 
506
137
  ErrorInfoNote.propTypes = {
@@ -522,11 +153,5 @@ ErrorInfoNote.propTypes = {
522
153
  }),
523
154
  ]),
524
155
  }),
525
- onErrorClick: PropTypes.func,
526
- onClose: PropTypes.func,
527
- isLiquidEnabled: PropTypes.bool,
528
- intl: PropTypes.object,
529
- useLegacyDisplay: PropTypes.bool, // Use simple list display for BEE Editor
530
156
  };
531
-
532
157
  export default injectIntl(ErrorInfoNote);
@@ -6,28 +6,6 @@
6
6
  import { defineMessages } from "react-intl";
7
7
  const scope = "creatives.componentsV2.ErrorInfoNote";
8
8
  export default defineMessages({
9
- // Tab labels for new tabbed interface
10
- htmlIssues: {
11
- id: `${scope}.htmlIssues`,
12
- defaultMessage: "HTML issues",
13
- },
14
- labelIssues: {
15
- id: `${scope}.labelIssues`,
16
- defaultMessage: "Label issues",
17
- },
18
- liquidIssues: {
19
- id: `${scope}.liquidIssues`,
20
- defaultMessage: "Liquid issues",
21
- },
22
- navigateToError: {
23
- id: `${scope}.navigateToError`,
24
- defaultMessage: "Go to error location",
25
- },
26
- closePanel: {
27
- id: `${scope}.closePanel`,
28
- defaultMessage: "Close validation panel",
29
- },
30
- // Legacy messages (kept for backwards compatibility)
31
9
  dynamicErrorHeader: {
32
10
  id: `${scope}.dynamicErrorHeader`,
33
11
  defaultMessage: