@capillarytech/creatives-library 8.0.249 → 8.0.250-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) 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 +18 -0
  8. package/utils/common.js +5 -0
  9. package/utils/commonUtils.js +28 -5
  10. package/utils/tests/commonUtil.test.js +224 -0
  11. package/utils/transformTemplateConfig.js +0 -10
  12. package/v2Components/CapDeviceContent/index.js +61 -56
  13. package/v2Components/CapTagList/index.js +6 -1
  14. package/v2Components/CapTagListWithInput/index.js +5 -1
  15. package/v2Components/CapTagListWithInput/messages.js +1 -1
  16. package/v2Components/CapWhatsappCTA/tests/index.test.js +5 -0
  17. package/v2Components/ErrorInfoNote/index.js +452 -72
  18. package/v2Components/ErrorInfoNote/messages.js +22 -0
  19. package/v2Components/ErrorInfoNote/style.scss +280 -4
  20. package/v2Components/FormBuilder/tests/index.test.js +13 -4
  21. package/v2Components/HtmlEditor/HTMLEditor.js +640 -94
  22. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +874 -0
  23. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1167 -133
  24. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +27 -16
  25. package/v2Components/HtmlEditor/_htmlEditor.scss +108 -45
  26. package/v2Components/HtmlEditor/_index.lazy.scss +1 -1
  27. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +13 -101
  28. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +148 -139
  29. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +2 -1
  30. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
  31. package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +9 -0
  32. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +1 -1
  33. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +22 -0
  34. package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +4 -7
  35. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +35 -45
  36. package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +1 -3
  37. package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
  38. package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +7 -6
  39. package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +3 -6
  40. package/v2Components/HtmlEditor/components/PreviewPane/index.js +11 -13
  41. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  42. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +49 -31
  43. package/v2Components/HtmlEditor/components/ValidationPanel/index.js +68 -39
  44. package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +254 -0
  45. package/v2Components/HtmlEditor/components/ValidationTabs/index.js +391 -0
  46. package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +51 -0
  47. package/v2Components/HtmlEditor/constants.js +42 -20
  48. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +373 -16
  49. package/v2Components/HtmlEditor/hooks/__tests__/useValidation.apiErrors.test.js +795 -0
  50. package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
  51. package/v2Components/HtmlEditor/hooks/useInAppContent.js +88 -146
  52. package/v2Components/HtmlEditor/hooks/useValidation.js +189 -53
  53. package/v2Components/HtmlEditor/index.js +1 -1
  54. package/v2Components/HtmlEditor/messages.js +95 -85
  55. package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +94 -45
  56. package/v2Components/HtmlEditor/utils/contentSanitizer.js +40 -41
  57. package/v2Components/HtmlEditor/utils/htmlValidator.js +71 -72
  58. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +134 -102
  59. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +23 -25
  60. package/v2Components/HtmlEditor/utils/validationAdapter.js +66 -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/_testAndPreviewSlidebox.scss +1 -0
  66. package/v2Containers/BeeEditor/index.js +172 -90
  67. package/v2Containers/BeePopupEditor/constants.js +10 -0
  68. package/v2Containers/BeePopupEditor/index.js +193 -0
  69. package/v2Containers/BeePopupEditor/tests/index.test.js +627 -0
  70. package/v2Containers/CreativesContainer/SlideBoxContent.js +127 -51
  71. package/v2Containers/CreativesContainer/SlideBoxFooter.js +163 -13
  72. package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -1
  73. package/v2Containers/CreativesContainer/constants.js +1 -0
  74. package/v2Containers/CreativesContainer/index.js +239 -46
  75. package/v2Containers/CreativesContainer/messages.js +8 -0
  76. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +11 -2
  77. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +38 -50
  78. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +106 -0
  79. package/v2Containers/Email/actions.js +7 -0
  80. package/v2Containers/Email/constants.js +5 -1
  81. package/v2Containers/Email/index.js +222 -27
  82. package/v2Containers/Email/messages.js +32 -0
  83. package/v2Containers/Email/reducer.js +12 -1
  84. package/v2Containers/Email/sagas.js +61 -7
  85. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +2 -0
  86. package/v2Containers/Email/tests/sagas.test.js +320 -29
  87. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1321 -0
  88. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +210 -15
  89. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +40 -74
  90. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +1749 -0
  91. package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +520 -0
  92. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +2 -67
  93. package/v2Containers/EmailWrapper/constants.js +2 -0
  94. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +629 -77
  95. package/v2Containers/EmailWrapper/index.js +103 -23
  96. package/v2Containers/EmailWrapper/messages.js +61 -1
  97. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +643 -0
  98. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +594 -77
  99. package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +376 -0
  100. package/v2Containers/InApp/__tests__/sagas.test.js +363 -0
  101. package/v2Containers/InApp/actions.js +7 -0
  102. package/v2Containers/InApp/constants.js +20 -4
  103. package/v2Containers/InApp/index.js +802 -359
  104. package/v2Containers/InApp/index.scss +4 -3
  105. package/v2Containers/InApp/messages.js +7 -3
  106. package/v2Containers/InApp/reducer.js +21 -3
  107. package/v2Containers/InApp/sagas.js +29 -9
  108. package/v2Containers/InApp/selectors.js +25 -5
  109. package/v2Containers/InApp/tests/index.test.js +154 -50
  110. package/v2Containers/InApp/tests/reducer.test.js +34 -0
  111. package/v2Containers/InApp/tests/sagas.test.js +61 -9
  112. package/v2Containers/InApp/tests/selectors.test.js +612 -0
  113. package/v2Containers/InAppWrapper/components/InAppWrapperView.js +162 -0
  114. package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +267 -0
  115. package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +9 -0
  116. package/v2Containers/InAppWrapper/constants.js +16 -0
  117. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +473 -0
  118. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +198 -0
  119. package/v2Containers/InAppWrapper/index.js +148 -0
  120. package/v2Containers/InAppWrapper/messages.js +49 -0
  121. package/v2Containers/InappAdvance/index.js +1099 -0
  122. package/v2Containers/InappAdvance/index.scss +10 -0
  123. package/v2Containers/InappAdvance/tests/index.test.js +448 -0
  124. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -0
  125. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -0
  126. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -0
  127. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -0
  128. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -0
  129. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -0
  130. package/v2Containers/TagList/index.js +62 -19
  131. package/v2Containers/Templates/_templates.scss +60 -1
  132. package/v2Containers/Templates/index.js +89 -4
  133. package/v2Containers/Templates/messages.js +4 -0
  134. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -0
  135. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +0 -152
  136. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +0 -214
@@ -1,18 +1,15 @@
1
1
  /**
2
- * ValidationErrorDisplay - HTML Editor validation using ErrorInfoNote
2
+ * ValidationErrorDisplay - HTML Editor validation display
3
3
  *
4
- * This component integrates the existing ErrorInfoNote component with the HTML Editor's
5
- * validation system, providing a consistent error display that matches the Figma design.
4
+ * This component displays validation errors using the new ValidationTabs component
5
+ * with tabbed interface for HTML, Label, and Liquid issues.
6
6
  */
7
7
 
8
- import React from 'react';
8
+ import React, { useState } from 'react';
9
9
  import PropTypes from 'prop-types';
10
10
 
11
- import CapRow from '@capillarytech/cap-ui-library/CapRow';
12
- import ErrorInfoNote from '../../../ErrorInfoNote';
13
-
14
- import { transformValidationToErrorInfo, hasValidationErrors } from '../../utils/validationAdapter';
15
- import { HTML_EDITOR_VARIANTS } from '../../constants';
11
+ import { hasValidationErrors } from '../../utils/validationAdapter';
12
+ import ValidationTabs from '../ValidationTabs';
16
13
 
17
14
  // Styles
18
15
  import './_validationErrorDisplay.scss';
@@ -20,51 +17,72 @@ import './_validationErrorDisplay.scss';
20
17
  /**
21
18
  * ValidationErrorDisplay Component
22
19
  *
23
- * Displays validation errors using the existing ErrorInfoNote component
20
+ * Displays validation errors using the ValidationTabs component
24
21
  */
25
22
  const ValidationErrorDisplay = ({
26
23
  validation,
27
- variant = HTML_EDITOR_VARIANTS.EMAIL,
28
24
  onErrorClick,
29
- className = ''
25
+ onClose,
26
+ isLiquidEnabled = false,
27
+ className = '',
30
28
  }) => {
31
- // Don't render if no validation or no errors
32
- if (!hasValidationErrors(validation)) {
33
- return null;
34
- }
29
+ // Track if panel is dismissed
30
+ const [isDismissed, setIsDismissed] = useState(false);
35
31
 
36
- // Transform validation data to ErrorInfoNote format
37
- const errorData = transformValidationToErrorInfo(validation, variant);
38
- const { errorMessages } = errorData || {};
39
-
40
- // Handle error click if provided
41
- const handleErrorClick = (error) => {
42
- onErrorClick?.(error);
32
+ // Handle close - dismiss temporarily
33
+ const handleClose = () => {
34
+ setIsDismissed(true);
35
+ if (onClose) {
36
+ onClose();
37
+ }
43
38
  };
44
39
 
40
+ // Reset dismissed state when validation changes (new errors appear)
41
+ React.useEffect(() => {
42
+ if (hasValidationErrors(validation)) {
43
+ setIsDismissed(false);
44
+ }
45
+ }, [validation]);
46
+
47
+ // Don't render if no validation, no errors, or dismissed
48
+ if (!hasValidationErrors(validation) || isDismissed) {
49
+ return null;
50
+ }
51
+
45
52
  return (
46
- <CapRow
53
+ <div
47
54
  className={`validation-error-display ${className}`}
48
55
  role="alert"
49
56
  aria-live="polite"
50
57
  aria-label="Validation errors"
51
58
  >
52
- <ErrorInfoNote
53
- errorMessages={errorMessages}
54
- onErrorClick={handleErrorClick}
59
+ <ValidationTabs
60
+ validation={validation}
61
+ onErrorClick={onErrorClick}
62
+ onClose={handleClose}
63
+ isLiquidEnabled={isLiquidEnabled}
55
64
  />
56
- </CapRow>
65
+ </div>
57
66
  );
58
67
  };
59
68
 
60
69
  ValidationErrorDisplay.propTypes = {
61
70
  validation: PropTypes.shape({
62
71
  isValidating: PropTypes.bool,
63
- getAllIssues: PropTypes.func
72
+ getAllIssues: PropTypes.func,
64
73
  }),
65
- variant: PropTypes.oneOf(Object.values(HTML_EDITOR_VARIANTS)),
66
74
  onErrorClick: PropTypes.func,
67
- className: PropTypes.string
75
+ onClose: PropTypes.func,
76
+ isLiquidEnabled: PropTypes.bool,
77
+ className: PropTypes.string,
78
+ };
79
+
80
+ ValidationErrorDisplay.defaultProps = {
81
+ validation: null,
82
+ onErrorClick: null,
83
+ onClose: null,
84
+ isLiquidEnabled: false,
85
+ className: '',
68
86
  };
69
87
 
70
88
  export default ValidationErrorDisplay;
@@ -21,9 +21,10 @@ import ShieldOutlined from '@ant-design/icons/ShieldOutlined';
21
21
  import BugOutlined from '@ant-design/icons/BugOutlined';
22
22
  import CodeOutlined from '@ant-design/icons/CodeOutlined';
23
23
  import EyeInvisibleOutlined from '@ant-design/icons/EyeInvisibleOutlined';
24
- import CheckCircleOutlined from '@ant-design/icons/CheckCircleOutlined';
24
+ import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
25
25
 
26
26
  import messages from './messages';
27
+ import { BLOCKING_ERROR_RULE_IDS } from '../../constants';
27
28
  import './_validationPanel.scss';
28
29
 
29
30
  const { Panel } = Collapse;
@@ -37,7 +38,7 @@ const ValidationPanel = ({
37
38
  onErrorClick,
38
39
  showLineNumbers = true,
39
40
  groupBySource = false,
40
- variant = 'email'
41
+ variant = 'email',
41
42
  }) => {
42
43
  const [activeKeys, setActiveKeys] = useState(['errors', 'warnings']);
43
44
 
@@ -56,31 +57,50 @@ const ValidationPanel = ({
56
57
  groups[source].push(issue);
57
58
  return groups;
58
59
  }, {});
59
- } else {
60
- return {
61
- errors: allIssues.filter(issue => issue.severity === 'error'),
62
- warnings: allIssues.filter(issue => issue.severity === 'warning'),
63
- info: allIssues.filter(issue => issue.severity === 'info')
64
- };
65
60
  }
61
+ return {
62
+ errors: allIssues.filter((issue) => issue.severity === 'error'),
63
+ warnings: allIssues.filter((issue) => issue.severity === 'warning'),
64
+ info: allIssues.filter((issue) => issue.severity === 'info'),
65
+ };
66
66
  }, [validation, groupBySource]);
67
67
 
68
+ // Check if an issue is a blocking error (API error, Rule Group #1, or client-side Liquid validation errors)
69
+ const isBlockingError = (issue) => {
70
+ const { rule, source, severity } = issue || {};
71
+ // API errors are blocking
72
+ if (rule === 'liquid-api-validation' || rule === 'standard-api-validation') {
73
+ return true;
74
+ }
75
+ // Client-side Liquid validation errors are blocking (genuine syntax errors)
76
+ if (source === 'liquid-validator' && severity === 'error') {
77
+ return true;
78
+ }
79
+ // Rule Group #1 errors are blocking
80
+ if (BLOCKING_ERROR_RULE_IDS.includes(rule)) {
81
+ return true;
82
+ }
83
+ return false;
84
+ };
85
+
68
86
  // Get icon for issue type
69
- const getIssueIcon = (severity, source) => {
87
+ // Blocking errors use error-icon, warnings use alert-warning
88
+ const getIssueIcon = (issue) => {
89
+ const { source, severity } = issue || {};
70
90
  if (source === 'security') {
71
91
  return <ShieldOutlined style={{ color: '#ff4d4f' }} />;
72
92
  }
73
93
 
74
- switch (severity) {
75
- case 'error':
76
- return <ExclamationCircleOutlined style={{ color: '#ff4d4f' }} />;
77
- case 'warning':
78
- return <WarningOutlined style={{ color: '#faad14' }} />;
79
- case 'info':
80
- return <InfoCircleOutlined style={{ color: '#1890ff' }} />;
81
- default:
82
- return <BugOutlined style={{ color: '#666' }} />;
94
+ // Only show error icon for blocking errors (API errors or Rule Group #1)
95
+ if (isBlockingError(issue)) {
96
+ return <CapIcon type="error-icon" style={{ color: '#ff4d4f' }} />;
97
+ }
98
+
99
+ // All other issues show as warnings
100
+ if (severity === 'info') {
101
+ return <CapIcon type="info" style={{ color: '#1890ff' }} />;
83
102
  }
103
+ return <CapIcon type="alert-warning" style={{ color: '#faad14' }} />;
84
104
  };
85
105
 
86
106
  // Get source icon
@@ -106,7 +126,7 @@ const ValidationPanel = ({
106
126
  line: issue.line,
107
127
  column: issue.column || 1,
108
128
  message: issue.message,
109
- severity: issue.severity
129
+ severity: issue.severity,
110
130
  });
111
131
  }
112
132
  };
@@ -126,7 +146,7 @@ const ValidationPanel = ({
126
146
  }}
127
147
  >
128
148
  <div className="validation-issue__icon">
129
- {getIssueIcon(issue.severity, issue.source)}
149
+ {getIssueIcon(issue)}
130
150
  </div>
131
151
 
132
152
  <div className="validation-issue__content">
@@ -137,10 +157,12 @@ const ValidationPanel = ({
137
157
  <div className="validation-issue__meta">
138
158
  {showLineNumbers && issue.line && (
139
159
  <span className="validation-issue__location">
140
- <FormattedMessage {...messages.lineColumn} values={{
141
- line: issue.line,
142
- column: issue.column || 1
143
- }} />
160
+ <FormattedMessage
161
+ {...messages.lineColumn}
162
+ values={{
163
+ line: issue.line,
164
+ column: issue.column || 1,
165
+ }} />
144
166
  </span>
145
167
  )}
146
168
 
@@ -162,17 +184,24 @@ const ValidationPanel = ({
162
184
  );
163
185
 
164
186
  // Render panel header with count
165
- const renderPanelHeader = (title, count, severity) => (
166
- <div className="validation-panel__header">
167
- <span className="validation-panel__title">
168
- {getIssueIcon(severity)}
169
- <FormattedMessage {...title} />
170
- </span>
171
- {count > 0 && (
172
- <Badge count={count} style={{ backgroundColor: getSeverityColor(severity) }} />
173
- )}
174
- </div>
175
- );
187
+ const renderPanelHeader = (title, count, severity, issues = []) => {
188
+ // Check if any issue in this group is a blocking error
189
+ const hasBlocking = issues.some((issue) => isBlockingError(issue));
190
+ // Use blocking error icon only if there are blocking errors, otherwise use warning icon
191
+ const iconIssue = hasBlocking ? { rule: 'blocking', source: 'blocking' } : { severity: 'warning' };
192
+
193
+ return (
194
+ <div className="validation-panel__header">
195
+ <span className="validation-panel__title">
196
+ {getIssueIcon(iconIssue)}
197
+ <FormattedMessage {...title} />
198
+ </span>
199
+ {count > 0 && (
200
+ <Badge count={count} style={{ backgroundColor: getSeverityColor(severity) }} />
201
+ )}
202
+ </div>
203
+ );
204
+ };
176
205
 
177
206
  // Get severity color
178
207
  const getSeverityColor = (severity) => {
@@ -241,14 +270,14 @@ const ValidationPanel = ({
241
270
  : messages[key] || { id: `htmlEditor.validation.${key}`, defaultMessage: key };
242
271
 
243
272
  const severity = isSourceGroup
244
- ? (issues.find(i => i.severity === 'error') ? 'error' :
245
- issues.find(i => i.severity === 'warning') ? 'warning' : 'info')
273
+ ? (issues.find((i) => i.severity === 'error') ? 'error'
274
+ : issues.find((i) => i.severity === 'warning') ? 'warning' : 'info')
246
275
  : key;
247
276
 
248
277
  return (
249
278
  <Panel
250
279
  key={key}
251
- header={renderPanelHeader(title, issues.length, severity)}
280
+ header={renderPanelHeader(title, issues.length, severity, issues)}
252
281
  className={`validation-panel__panel validation-panel__panel--${severity}`}
253
282
  >
254
283
  <div className="validation-panel__issues">
@@ -291,7 +320,7 @@ ValidationPanel.propTypes = {
291
320
  onErrorClick: PropTypes.func,
292
321
  showLineNumbers: PropTypes.bool,
293
322
  groupBySource: PropTypes.bool,
294
- variant: PropTypes.oneOf(['email', 'inapp'])
323
+ variant: PropTypes.oneOf(['email', 'inapp']),
295
324
  };
296
325
 
297
326
  export default ValidationPanel;
@@ -0,0 +1,254 @@
1
+ /**
2
+ * ValidationTabs Styles
3
+ */
4
+
5
+ @import '~@capillarytech/cap-ui-library/styles/_variables.scss';
6
+
7
+ .validation-tabs {
8
+ overflow-y: hidden;
9
+ width: 100%;
10
+ background-color: $CAP_COLOR_05; // Light pink background
11
+ border-radius: 0.25rem;
12
+ padding: 1rem 1rem;
13
+ box-sizing: border-box;
14
+
15
+ &__header {
16
+ display: flex;
17
+ align-items: flex-start;
18
+ justify-content: space-between;
19
+ width: 100%;
20
+ }
21
+
22
+ &__tabs {
23
+ flex: 1;
24
+
25
+ // Override CapTab styles for proper spacing
26
+ .cap-tab-v2 {
27
+ .ant-tabs-nav {
28
+ margin-bottom: 0;
29
+
30
+ &::before {
31
+ border-bottom: none;
32
+ }
33
+ }
34
+
35
+ .ant-tabs-tab {
36
+ padding: 0.5rem 0.75rem; // Add horizontal padding for spacing
37
+ margin-right: 0; // Remove margin, use padding instead
38
+ color: $CAP_G03;
39
+ font-size: 0.875rem;
40
+ font-weight: 500;
41
+
42
+ & + .ant-tabs-tab {
43
+ margin-left: 1.5rem; // Add space between tabs
44
+ }
45
+
46
+ &:hover {
47
+ color: $CAP_COLOR_05;
48
+ background: transparent;
49
+ }
50
+
51
+ &.ant-tabs-tab-active {
52
+ .ant-tabs-tab-btn {
53
+ color: $CAP_COLOR_05;
54
+ font-weight: 600;
55
+ }
56
+ }
57
+ }
58
+
59
+ .ant-tabs-ink-bar {
60
+ background-color: $CAP_COLOR_05;
61
+ height: 0.125rem;
62
+ }
63
+
64
+ .ant-tabs-content-holder {
65
+ padding-top: 0.25rem; // Reduced from 0.5rem
66
+ padding-bottom: 0; // No bottom padding
67
+ }
68
+ }
69
+ }
70
+
71
+ &__tab-label {
72
+ display: flex;
73
+ align-items: center;
74
+ gap: 0.25rem;
75
+ }
76
+
77
+ &__tab-count {
78
+ color: inherit;
79
+ }
80
+
81
+ &__actions {
82
+ display: flex;
83
+ align-items: center;
84
+ gap: 0.5rem;
85
+ flex-shrink: 0;
86
+ padding-top: 0.5rem;
87
+ }
88
+
89
+ &__close {
90
+ display: flex;
91
+ align-items: center;
92
+ justify-content: center;
93
+ width: 1.5rem;
94
+ height: 1.5rem;
95
+ padding: 0;
96
+ background: transparent;
97
+ border: none;
98
+ border-radius: 0.25rem;
99
+ cursor: pointer;
100
+ color: $CAP_G03;
101
+ transition: all 0.2s ease;
102
+
103
+ &:hover {
104
+ background-color: rgba($CAP_COLOR_05, 0.1);
105
+ color: $CAP_COLOR_05;
106
+ }
107
+
108
+ .cap-icon-v2 {
109
+ font-size: 0.875rem;
110
+ }
111
+ }
112
+
113
+ &__content {
114
+ max-height: 15rem; // Limit height for many errors
115
+ overflow-y: auto;
116
+ padding-right: 0.25rem;
117
+ padding-bottom: 0; // Remove bottom padding completely
118
+
119
+ // Custom scrollbar
120
+ &::-webkit-scrollbar {
121
+ width: 0.375rem;
122
+ }
123
+
124
+ &::-webkit-scrollbar-track {
125
+ background: transparent;
126
+ }
127
+
128
+ &::-webkit-scrollbar-thumb {
129
+ background-color: $CAP_G06;
130
+ border-radius: 0.1875rem;
131
+
132
+ &:hover {
133
+ background-color: $CAP_G04;
134
+ }
135
+ }
136
+ }
137
+
138
+ &__item {
139
+ display: flex;
140
+ align-items: flex-start;
141
+ gap: 0.5rem;
142
+ padding: 0.25rem 0; // Reduced from 0.375rem
143
+ border-bottom: 1px solid rgba($CAP_G06, 0.3);
144
+
145
+ &:last-child {
146
+ border-bottom: none;
147
+ padding-bottom: 0.25rem; // Minimal padding on last item
148
+ }
149
+
150
+ &--error {
151
+ .validation-tabs__icon--error {
152
+ color: $CAP_RED;
153
+ }
154
+ }
155
+
156
+ &--warning {
157
+ .validation-tabs__icon--warning {
158
+ color: $CAP_YELLOW;
159
+ }
160
+ }
161
+ }
162
+
163
+ &__item-icon {
164
+ flex-shrink: 0;
165
+ display: flex;
166
+ align-items: center;
167
+ padding-top: 0.125rem;
168
+ }
169
+
170
+ &__icon {
171
+ font-size: 0.875rem;
172
+
173
+ &--error {
174
+ color: $CAP_RED;
175
+ }
176
+
177
+ &--warning {
178
+ color: $CAP_YELLOW;
179
+ }
180
+ }
181
+
182
+ &__item-content {
183
+ flex: 1;
184
+ display: flex;
185
+ flex-wrap: wrap;
186
+ align-items: baseline;
187
+ gap: 0.25rem;
188
+ font-size: 0.75rem;
189
+ line-height: 1.4;
190
+ color: $CAP_G01;
191
+ }
192
+
193
+ &__item-message {
194
+ color: $CAP_G01;
195
+ }
196
+
197
+ &__item-location {
198
+ color: $CAP_G03;
199
+ white-space: nowrap;
200
+ }
201
+
202
+ &__item-rule {
203
+ color: $CAP_G04;
204
+ font-family: monospace;
205
+ font-size: 0.6875rem;
206
+ }
207
+
208
+ &__item-navigate {
209
+ flex-shrink: 0;
210
+ display: flex;
211
+ align-items: center;
212
+ justify-content: center;
213
+ width: 1.25rem;
214
+ height: 1.25rem;
215
+ padding: 0;
216
+ background: transparent;
217
+ border: none;
218
+ border-radius: 0.25rem;
219
+ cursor: pointer;
220
+ color: $CAP_G04;
221
+ transition: all 0.2s ease;
222
+
223
+ &:hover {
224
+ background-color: rgba($CAP_G01, 0.1);
225
+ color: $CAP_G01;
226
+ }
227
+
228
+ .anticon {
229
+ font-size: 0.75rem;
230
+ }
231
+ }
232
+ }
233
+
234
+ // Responsive adjustments
235
+ @media (max-width: 768px) {
236
+ .validation-tabs {
237
+ padding: 0.375rem 0.5rem;
238
+
239
+ &__tabs {
240
+ .ant-tabs-tab {
241
+ margin-right: 1rem;
242
+ font-size: 0.8125rem;
243
+ }
244
+ }
245
+
246
+ &__content {
247
+ max-height: 10rem;
248
+ }
249
+
250
+ &__item-content {
251
+ font-size: 0.6875rem;
252
+ }
253
+ }
254
+ }