@capillarytech/creatives-library 8.0.242-alpha.0 → 8.0.242-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 (215) 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/sagas/__tests__/assetPolling.test.js +74 -3
  7. package/sagas/assetPolling.js +8 -1
  8. package/services/api.js +10 -5
  9. package/services/tests/api.test.js +18 -0
  10. package/translations/en.json +0 -1
  11. package/utils/common.js +5 -0
  12. package/utils/commonUtils.js +14 -1
  13. package/utils/tests/commonUtil.test.js +224 -0
  14. package/utils/transformTemplateConfig.js +0 -10
  15. package/utils/transformerUtils.js +0 -42
  16. package/v2Components/CapDeviceContent/index.js +61 -56
  17. package/v2Components/CapImageUpload/constants.js +0 -2
  18. package/v2Components/CapImageUpload/index.js +14 -54
  19. package/v2Components/CapImageUpload/index.scss +1 -4
  20. package/v2Components/CapImageUpload/messages.js +0 -4
  21. package/v2Components/CapTagList/index.js +6 -1
  22. package/v2Components/CapTagListWithInput/index.js +5 -1
  23. package/v2Components/CapTagListWithInput/messages.js +1 -1
  24. package/v2Components/CapWhatsappCTA/tests/index.test.js +5 -0
  25. package/v2Components/ErrorInfoNote/index.js +412 -72
  26. package/v2Components/ErrorInfoNote/messages.js +22 -0
  27. package/v2Components/ErrorInfoNote/style.scss +279 -2
  28. package/v2Components/HtmlEditor/HTMLEditor.js +210 -89
  29. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1132 -133
  30. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +17 -12
  31. package/v2Components/HtmlEditor/_htmlEditor.scss +8 -23
  32. package/v2Components/HtmlEditor/_index.lazy.scss +1 -1
  33. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +13 -101
  34. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +148 -139
  35. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +2 -1
  36. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
  37. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +1 -1
  38. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +1 -0
  39. package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +4 -7
  40. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +35 -45
  41. package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +1 -3
  42. package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
  43. package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +7 -6
  44. package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +3 -6
  45. package/v2Components/HtmlEditor/components/PreviewPane/index.js +10 -11
  46. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  47. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +87 -62
  48. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +49 -31
  49. package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +254 -0
  50. package/v2Components/HtmlEditor/components/ValidationTabs/index.js +362 -0
  51. package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +51 -0
  52. package/v2Components/HtmlEditor/constants.js +29 -20
  53. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +373 -16
  54. package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
  55. package/v2Components/HtmlEditor/hooks/useInAppContent.js +88 -146
  56. package/v2Components/HtmlEditor/index.js +1 -1
  57. package/v2Components/HtmlEditor/messages.js +95 -85
  58. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +99 -101
  59. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +23 -25
  60. package/v2Components/HtmlEditor/utils/validationAdapter.js +34 -41
  61. package/v2Components/MobilePushPreviewV2/index.js +32 -7
  62. package/v2Components/TemplatePreview/_templatePreview.scss +44 -24
  63. package/v2Components/TemplatePreview/index.js +47 -32
  64. package/v2Components/TemplatePreview/messages.js +4 -0
  65. package/v2Components/TestAndPreviewSlidebox/index.js +31 -25
  66. package/v2Containers/App/constants.js +0 -5
  67. package/v2Containers/BeeEditor/index.js +82 -80
  68. package/v2Containers/BeePopupEditor/constants.js +10 -0
  69. package/v2Containers/BeePopupEditor/index.js +193 -0
  70. package/v2Containers/BeePopupEditor/tests/index.test.js +627 -0
  71. package/v2Containers/Cap/tests/__snapshots__/index.test.js.snap +0 -1
  72. package/v2Containers/CreativesContainer/SlideBoxContent.js +148 -120
  73. package/v2Containers/CreativesContainer/SlideBoxFooter.js +9 -3
  74. package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -2
  75. package/v2Containers/CreativesContainer/constants.js +1 -2
  76. package/v2Containers/CreativesContainer/index.js +173 -193
  77. package/v2Containers/CreativesContainer/messages.js +4 -4
  78. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +38 -50
  79. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +36 -0
  80. package/v2Containers/Email/actions.js +7 -0
  81. package/v2Containers/Email/constants.js +5 -1
  82. package/v2Containers/Email/index.js +13 -0
  83. package/v2Containers/Email/messages.js +32 -0
  84. package/v2Containers/Email/reducer.js +12 -1
  85. package/v2Containers/Email/sagas.js +41 -6
  86. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +2 -0
  87. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1045 -0
  88. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +193 -7
  89. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +40 -74
  90. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +2 -67
  91. package/v2Containers/EmailWrapper/constants.js +2 -0
  92. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +436 -67
  93. package/v2Containers/EmailWrapper/index.js +99 -23
  94. package/v2Containers/EmailWrapper/messages.js +61 -1
  95. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +26 -1
  96. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +111 -77
  97. package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +376 -0
  98. package/v2Containers/InApp/__tests__/sagas.test.js +363 -0
  99. package/v2Containers/InApp/actions.js +7 -0
  100. package/v2Containers/InApp/constants.js +20 -4
  101. package/v2Containers/InApp/index.js +800 -357
  102. package/v2Containers/InApp/index.scss +4 -3
  103. package/v2Containers/InApp/messages.js +7 -3
  104. package/v2Containers/InApp/reducer.js +21 -3
  105. package/v2Containers/InApp/sagas.js +29 -9
  106. package/v2Containers/InApp/selectors.js +25 -5
  107. package/v2Containers/InApp/tests/index.test.js +154 -50
  108. package/v2Containers/InApp/tests/reducer.test.js +34 -0
  109. package/v2Containers/InApp/tests/sagas.test.js +61 -9
  110. package/v2Containers/InApp/tests/selectors.test.js +612 -0
  111. package/v2Containers/InAppWrapper/components/InAppWrapperView.js +162 -0
  112. package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +267 -0
  113. package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +9 -0
  114. package/v2Containers/InAppWrapper/constants.js +16 -0
  115. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +473 -0
  116. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +198 -0
  117. package/v2Containers/InAppWrapper/index.js +148 -0
  118. package/v2Containers/InAppWrapper/messages.js +49 -0
  119. package/v2Containers/InappAdvance/index.js +1099 -0
  120. package/v2Containers/InappAdvance/index.scss +10 -0
  121. package/v2Containers/InappAdvance/tests/index.test.js +448 -0
  122. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -3
  123. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -2
  124. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -25
  125. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -18
  126. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -46
  127. package/v2Containers/SmsTrai/Create/tests/__snapshots__/index.test.js.snap +0 -4
  128. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -8
  129. package/v2Containers/TagList/index.js +67 -1
  130. package/v2Containers/Templates/ChannelTypeIllustration.js +1 -13
  131. package/v2Containers/Templates/_templates.scss +56 -200
  132. package/v2Containers/Templates/actions.js +1 -2
  133. package/v2Containers/Templates/constants.js +0 -1
  134. package/v2Containers/Templates/index.js +124 -277
  135. package/v2Containers/Templates/messages.js +4 -24
  136. package/v2Containers/Templates/reducer.js +0 -2
  137. package/v2Containers/Templates/tests/index.test.js +0 -10
  138. package/v2Containers/TemplatesV2/index.js +2 -3
  139. package/v2Containers/TemplatesV2/messages.js +0 -4
  140. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +35 -132
  141. package/v2Components/CapImageUrlUpload/constants.js +0 -19
  142. package/v2Components/CapImageUrlUpload/index.js +0 -455
  143. package/v2Components/CapImageUrlUpload/index.scss +0 -35
  144. package/v2Components/CapImageUrlUpload/messages.js +0 -47
  145. package/v2Containers/WebPush/Create/components/ButtonForm.js +0 -175
  146. package/v2Containers/WebPush/Create/components/ButtonItem.js +0 -101
  147. package/v2Containers/WebPush/Create/components/ButtonList.js +0 -144
  148. package/v2Containers/WebPush/Create/components/_buttons.scss +0 -246
  149. package/v2Containers/WebPush/Create/components/tests/ButtonForm.test.js +0 -554
  150. package/v2Containers/WebPush/Create/components/tests/ButtonItem.test.js +0 -607
  151. package/v2Containers/WebPush/Create/components/tests/ButtonList.test.js +0 -633
  152. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonForm.test.js.snap +0 -666
  153. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonItem.test.js.snap +0 -74
  154. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonList.test.js.snap +0 -80
  155. package/v2Containers/WebPush/Create/index.js +0 -1755
  156. package/v2Containers/WebPush/Create/index.scss +0 -123
  157. package/v2Containers/WebPush/Create/messages.js +0 -199
  158. package/v2Containers/WebPush/Create/preview/DevicePreviewContent.js +0 -241
  159. package/v2Containers/WebPush/Create/preview/NotificationContainer.js +0 -290
  160. package/v2Containers/WebPush/Create/preview/PreviewContent.js +0 -81
  161. package/v2Containers/WebPush/Create/preview/PreviewControls.js +0 -240
  162. package/v2Containers/WebPush/Create/preview/PreviewDisclaimer.js +0 -23
  163. package/v2Containers/WebPush/Create/preview/WebPushPreview.js +0 -144
  164. package/v2Containers/WebPush/Create/preview/assets/Light.svg +0 -53
  165. package/v2Containers/WebPush/Create/preview/assets/Top.svg +0 -5
  166. package/v2Containers/WebPush/Create/preview/assets/chrome-icon.png +0 -0
  167. package/v2Containers/WebPush/Create/preview/assets/edge-icon.png +0 -0
  168. package/v2Containers/WebPush/Create/preview/assets/firefox-icon.svg +0 -106
  169. package/v2Containers/WebPush/Create/preview/assets/iOS.svg +0 -26
  170. package/v2Containers/WebPush/Create/preview/assets/opera-icon.svg +0 -18
  171. package/v2Containers/WebPush/Create/preview/assets/safari-icon.svg +0 -29
  172. package/v2Containers/WebPush/Create/preview/components/AndroidMobileChromeHeader.js +0 -44
  173. package/v2Containers/WebPush/Create/preview/components/AndroidMobileExpanded.js +0 -110
  174. package/v2Containers/WebPush/Create/preview/components/IOSHeader.js +0 -45
  175. package/v2Containers/WebPush/Create/preview/components/NotificationExpandedContent.js +0 -72
  176. package/v2Containers/WebPush/Create/preview/components/NotificationHeader.js +0 -55
  177. package/v2Containers/WebPush/Create/preview/components/WindowsChromeExpanded.js +0 -70
  178. package/v2Containers/WebPush/Create/preview/components/tests/AndroidMobileExpanded.test.js +0 -512
  179. package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/AndroidMobileExpanded.test.js.snap +0 -77
  180. package/v2Containers/WebPush/Create/preview/config/notificationMappings.js +0 -527
  181. package/v2Containers/WebPush/Create/preview/constants.js +0 -162
  182. package/v2Containers/WebPush/Create/preview/notification-container.scss +0 -104
  183. package/v2Containers/WebPush/Create/preview/preview.scss +0 -409
  184. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-chrome.scss +0 -300
  185. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-edge.scss +0 -12
  186. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-firefox.scss +0 -12
  187. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-opera.scss +0 -12
  188. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-chrome.scss +0 -303
  189. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-edge.scss +0 -11
  190. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-firefox.scss +0 -11
  191. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-opera.scss +0 -11
  192. package/v2Containers/WebPush/Create/preview/styles/_base.scss +0 -188
  193. package/v2Containers/WebPush/Create/preview/styles/_ios.scss +0 -106
  194. package/v2Containers/WebPush/Create/preview/styles/_ipados.scss +0 -107
  195. package/v2Containers/WebPush/Create/preview/styles/_macos-chrome.scss +0 -75
  196. package/v2Containers/WebPush/Create/preview/styles/_windows-chrome.scss +0 -174
  197. package/v2Containers/WebPush/Create/preview/tests/DevicePreviewContent.test.js +0 -909
  198. package/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +0 -1077
  199. package/v2Containers/WebPush/Create/preview/tests/PreviewControls.test.js +0 -723
  200. package/v2Containers/WebPush/Create/preview/tests/WebPushPreview.test.js +0 -943
  201. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/DevicePreviewContent.test.js.snap +0 -128
  202. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/NotificationContainer.test.js.snap +0 -121
  203. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/PreviewControls.test.js.snap +0 -144
  204. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/WebPushPreview.test.js.snap +0 -127
  205. package/v2Containers/WebPush/Create/utils/urlValidation.js +0 -116
  206. package/v2Containers/WebPush/Create/utils/urlValidation.test.js +0 -449
  207. package/v2Containers/WebPush/actions.js +0 -60
  208. package/v2Containers/WebPush/constants.js +0 -108
  209. package/v2Containers/WebPush/index.js +0 -2
  210. package/v2Containers/WebPush/reducer.js +0 -104
  211. package/v2Containers/WebPush/sagas.js +0 -119
  212. package/v2Containers/WebPush/selectors.js +0 -65
  213. package/v2Containers/WebPush/tests/reducer.test.js +0 -863
  214. package/v2Containers/WebPush/tests/sagas.test.js +0 -566
  215. package/v2Containers/WebPush/tests/selectors.test.js +0 -960
@@ -1,21 +1,402 @@
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";
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';
8
9
  import {
9
10
  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
+ */
203
+ export const ErrorInfoNote = (props) => {
204
+ const {
205
+ errorMessages,
206
+ onErrorClick,
207
+ onClose,
208
+ isLiquidEnabled = true,
209
+ intl,
210
+ } = props;
211
+
212
+ const [isDismissed, setIsDismissed] = useState(false);
213
+ const [activeKey, setActiveKey] = useState(null);
214
+
215
+ const {
216
+ LIQUID_ERROR_MSG: rawLiquidErrors = [],
217
+ STANDARD_ERROR_MSG: rawStandardErrors = [],
218
+ } = errorMessages || {};
219
+
220
+ // Detect if platform-specific (ANDROID/IOS) or GENERIC
221
+ const isObject = typeof rawStandardErrors === 'object' && rawStandardErrors !== null;
222
+ const isNotArray = !Array.isArray(rawLiquidErrors);
223
+ const hasPlatformKeys = isObject && isNotArray && [ANDROID, IOS, GENERIC].some((key) => key in rawLiquidErrors);
224
+
225
+ // For platform-specific errors, use the legacy renderer
226
+ if (hasPlatformKeys) {
227
+ const androidErrors = {
228
+ STANDARD: processErrors(rawStandardErrors, 'standard', ANDROID, messages),
229
+ LIQUID: processErrors(rawLiquidErrors, 'liquid', ANDROID, messages),
230
+ };
231
+ const iosErrors = {
232
+ STANDARD: processErrors(rawStandardErrors, 'standard', IOS, messages),
233
+ LIQUID: processErrors(rawLiquidErrors, 'liquid', IOS, messages),
234
+ };
235
+ return (
236
+ <ErrorTypeRenderer
237
+ genericErrors={null}
238
+ androidErrors={androidErrors}
239
+ iosErrors={iosErrors}
240
+ ErrorSectionComponent={ErrorSection}
241
+ />
242
+ );
243
+ }
244
+
245
+ // Categorize errors for tabbed interface
246
+ const { htmlIssues, labelIssues, liquidIssues } = useMemo(() => categorizeErrorMessages(
247
+ Array.isArray(rawStandardErrors) ? rawStandardErrors : [],
248
+ Array.isArray(rawLiquidErrors) ? rawLiquidErrors : [],
249
+ ), [rawStandardErrors, rawLiquidErrors]);
250
+
251
+ // Calculate counts
252
+ const htmlCount = htmlIssues.length;
253
+ const labelCount = labelIssues.length;
254
+ const liquidCount = liquidIssues.length;
255
+ const totalCount = htmlCount + labelCount + (isLiquidEnabled ? liquidCount : 0);
256
+
257
+ // Set default active key
258
+ useMemo(() => {
259
+ if (!activeKey) {
260
+ if (htmlCount > 0) {
261
+ setActiveKey('html');
262
+ } else if (labelCount > 0) {
263
+ setActiveKey('label');
264
+ } else if (liquidCount > 0 && isLiquidEnabled) {
265
+ setActiveKey('liquid');
266
+ }
267
+ }
268
+ }, [htmlCount, labelCount, liquidCount, isLiquidEnabled, activeKey]);
269
+
270
+ // Handle close
271
+ const handleClose = () => {
272
+ setIsDismissed(true);
273
+ if (onClose) {
274
+ onClose();
275
+ }
276
+ };
277
+
278
+ // Handle liquid documentation click
279
+ const handleLiquidDocClick = () => {
280
+ window.open(
281
+ 'https://docs.capillarytech.com/docs/liquid-language-in-messages',
282
+ '_blank',
283
+ );
284
+ };
285
+
286
+ // Don't render if no issues or dismissed
287
+ if (totalCount === 0 || isDismissed) {
288
+ return null;
289
+ }
290
+
291
+ // Build tab panes (CapTab uses 'panes' with 'tab' and 'content' properties)
292
+ const tabPanes = [];
293
+
294
+ if (htmlCount > 0) {
295
+ tabPanes.push({
296
+ key: 'html',
297
+ tab: (
298
+ <span className="error-info-note__tab-label">
299
+ <FormattedMessage {...messages.htmlIssues} />
300
+ <span className="error-info-note__tab-count">
301
+ (
302
+ {htmlCount}
303
+ )
304
+ </span>
305
+ </span>
306
+ ),
307
+ content: (
308
+ <TabContent
309
+ issues={htmlIssues}
310
+ onErrorClick={onErrorClick}
311
+ intl={intl}
312
+ />
313
+ ),
314
+ });
315
+ }
316
+
317
+ if (labelCount > 0) {
318
+ tabPanes.push({
319
+ key: 'label',
320
+ tab: (
321
+ <span className="error-info-note__tab-label">
322
+ <FormattedMessage {...messages.labelIssues} />
323
+ <span className="error-info-note__tab-count">
324
+ (
325
+ {labelCount}
326
+ )
327
+ </span>
328
+ </span>
329
+ ),
330
+ content: (
331
+ <TabContent
332
+ issues={labelIssues}
333
+ onErrorClick={onErrorClick}
334
+ intl={intl}
335
+ />
336
+ ),
337
+ });
338
+ }
339
+
340
+ if (isLiquidEnabled && liquidCount > 0) {
341
+ tabPanes.push({
342
+ key: 'liquid',
343
+ tab: (
344
+ <span className="error-info-note__tab-label">
345
+ <FormattedMessage {...messages.liquidIssues} />
346
+ <span className="error-info-note__tab-count">
347
+ (
348
+ {liquidCount}
349
+ )
350
+ </span>
351
+ </span>
352
+ ),
353
+ content: (
354
+ <TabContent
355
+ issues={liquidIssues}
356
+ onErrorClick={onErrorClick}
357
+ intl={intl}
358
+ />
359
+ ),
360
+ });
361
+ }
362
+
363
+ return (
364
+ <div className="error-container error-container--tabs">
365
+ <CapRow className="error-info-note__header">
366
+ <CapTab
367
+ activeKey={activeKey || (tabPanes[0]?.key)}
368
+ onChange={setActiveKey}
369
+ panes={tabPanes}
370
+ className="error-info-note__tabs"
371
+ />
372
+ <CapRow className="error-info-note__actions">
373
+ {activeKey === 'liquid' && isLiquidEnabled && (
374
+ <CapButton
375
+ type="flat"
376
+ className="error-info-note__liquid-doc"
377
+ onClick={handleLiquidDocClick}
378
+ >
379
+ <FormattedMessage {...messages.liquidDoc} />
380
+ <CapIcon size="s" type="launch" />
381
+ </CapButton>
382
+ )}
383
+ <CapTooltip title={intl?.formatMessage ? intl.formatMessage({ id: 'errorInfoNote.closePanel', defaultMessage: 'Close validation panel' }) : 'Close validation panel'}>
384
+ <button
385
+ type="button"
386
+ className="error-info-note__close"
387
+ onClick={handleClose}
388
+ aria-label="Close validation panel"
389
+ >
390
+ <CapIcon type="close" />
391
+ </button>
392
+ </CapTooltip>
393
+ </CapRow>
394
+ </CapRow>
395
+ </div>
396
+ );
397
+ };
398
+
399
+ // Legacy ErrorSection component for platform-specific errors (backwards compatibility)
19
400
  const ErrorSection = ({
20
401
  title,
21
402
  errors,
@@ -26,7 +407,7 @@ const ErrorSection = ({
26
407
  {title && (
27
408
  <CapRow
28
409
  className={`error-header ${
29
- !liquidError ? "standard-error-header" : ""
410
+ !liquidError ? 'standard-error-header' : ''
30
411
  }`}
31
412
  >
32
413
  <>
@@ -38,8 +419,7 @@ const ErrorSection = ({
38
419
  <CapButton
39
420
  type="flat"
40
421
  className="add-btn"
41
- onClick={() => window.open("https://docs.capillarytech.com/docs/liquid-language-in-messages", "_blank")
42
- }
422
+ onClick={() => window.open('https://docs.capillarytech.com/docs/liquid-language-in-messages', '_blank')}
43
423
  >
44
424
  <FormattedMessage {...messages.liquidDoc} />
45
425
  <CapIcon size="s" type="launch" />
@@ -53,21 +433,15 @@ const ErrorSection = ({
53
433
  <CapLabel type="label2">{platformLabel}</CapLabel>
54
434
  </CapRow>
55
435
  )}
56
- <CapList
57
- className="error-list"
58
- size="small"
59
- dataSource={errors}
60
- renderItem={(error, index) => (
61
- <CapList.Item>
436
+ <div className="error-list-legacy">
437
+ {(errors || []).map((error) => (
438
+ <div key={`${error}`} className="error-list-legacy__item">
62
439
  <CapLabel type="label2" className="cap-list-v2-error-item">
63
- <CapLabel type="label2">
64
- <FormattedNumber value={index + 1} />.
65
- </CapLabel>
66
440
  <CapLabel type="label2">{error}</CapLabel>
67
441
  </CapLabel>
68
- </CapList.Item>
69
- )}
70
- />
442
+ </div>
443
+ ))}
444
+ </div>
71
445
  </>
72
446
  );
73
447
 
@@ -84,54 +458,15 @@ ErrorSection.defaultProps = {
84
458
  platformLabel: null,
85
459
  };
86
460
 
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
-
130
461
  ErrorInfoNote.defaultProps = {
131
462
  errorMessages: {
132
463
  LIQUID_ERROR_MSG: [],
133
464
  STANDARD_ERROR_MSG: [],
134
465
  },
466
+ onErrorClick: null,
467
+ onClose: null,
468
+ isLiquidEnabled: true,
469
+ intl: null,
135
470
  };
136
471
 
137
472
  ErrorInfoNote.propTypes = {
@@ -153,5 +488,10 @@ ErrorInfoNote.propTypes = {
153
488
  }),
154
489
  ]),
155
490
  }),
491
+ onErrorClick: PropTypes.func,
492
+ onClose: PropTypes.func,
493
+ isLiquidEnabled: PropTypes.bool,
494
+ intl: PropTypes.object,
156
495
  };
496
+
157
497
  export default injectIntl(ErrorInfoNote);
@@ -6,6 +6,28 @@
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)
9
31
  dynamicErrorHeader: {
10
32
  id: `${scope}.dynamicErrorHeader`,
11
33
  defaultMessage: