@capillarytech/creatives-library 8.0.262-alpha.0 → 8.0.262
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.
- package/package.json +1 -1
- package/v2Components/ErrorInfoNote/index.js +57 -112
- package/v2Components/ErrorInfoNote/messages.js +8 -12
- package/v2Components/ErrorInfoNote/style.scss +0 -4
- package/v2Components/HtmlEditor/HTMLEditor.js +46 -182
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +11 -15
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +7 -8
- package/v2Components/HtmlEditor/_htmlEditor.scss +6 -6
- package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +1 -1
- package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +4 -4
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +17 -0
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +15 -28
- package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +0 -4
- package/v2Components/HtmlEditor/components/ValidationPanel/index.js +7 -31
- package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +53 -26
- package/v2Components/HtmlEditor/components/ValidationTabs/index.js +74 -144
- package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +14 -14
- package/v2Components/HtmlEditor/hooks/useValidation.js +23 -3
- package/v2Components/HtmlEditor/utils/validationConstants.js +3 -4
- package/v2Containers/CreativesContainer/index.js +1 -3
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +6 -9
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +5 -49
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +23 -38
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +4 -2
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useMemo } from 'react';
|
|
1
|
+
import React, { useState, useMemo, useEffect } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
4
4
|
import CapButton from '@capillarytech/cap-ui-library/CapButton';
|
|
@@ -15,57 +15,45 @@ import messages from './messages';
|
|
|
15
15
|
import { processErrors } from './utils';
|
|
16
16
|
import ErrorTypeRenderer from './ErrorTypeRenderer';
|
|
17
17
|
import { ANDROID, GENERIC, IOS } from '../../v2Containers/CreativesContainer/constants';
|
|
18
|
-
import {
|
|
18
|
+
import { ERROR_TAB_KEYS } from '../HtmlEditor/utils/validationConstants';
|
|
19
19
|
import { SEVERITY } from '../HtmlEditor/components/ValidationPanel/constants';
|
|
20
20
|
import { LIQUID_DOC_URL } from './constants';
|
|
21
21
|
|
|
22
22
|
const { CapLabelInline } = CapLabel;
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* Group messages into Errors (blocking) and Warnings (non-blocking).
|
|
26
|
+
* STANDARD_ERROR_MSG + LIQUID_ERROR_MSG = errors; optional STANDARD_WARNING_MSG = warnings.
|
|
26
27
|
*/
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const labelIssues = [];
|
|
30
|
-
const liquidIssues = [];
|
|
31
|
-
|
|
32
|
-
// Process standard errors
|
|
33
|
-
(standardErrors || []).forEach((error, index) => {
|
|
34
|
-
const errorLower = (error || '').toLowerCase();
|
|
35
|
-
|
|
36
|
-
// Check if it's a Label (tag syntax) issue
|
|
37
|
-
const isLabelIssue = LABEL_ISSUE_PATTERNS.some(
|
|
38
|
-
(pattern) => errorLower.includes(pattern.toLowerCase()),
|
|
39
|
-
);
|
|
28
|
+
const groupByErrorsAndWarnings = (standardErrors, liquidErrors, standardWarnings = []) => {
|
|
29
|
+
const errors = [];
|
|
40
30
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
} else {
|
|
49
|
-
htmlIssues.push({
|
|
50
|
-
message: error,
|
|
51
|
-
severity: SEVERITY.ERROR,
|
|
52
|
-
index,
|
|
53
|
-
source: 'html',
|
|
54
|
-
});
|
|
55
|
-
}
|
|
31
|
+
(standardErrors || []).forEach((message, index) => {
|
|
32
|
+
errors.push({
|
|
33
|
+
message,
|
|
34
|
+
severity: SEVERITY.ERROR,
|
|
35
|
+
index,
|
|
36
|
+
source: 'standard',
|
|
37
|
+
});
|
|
56
38
|
});
|
|
57
39
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
message: error,
|
|
40
|
+
(liquidErrors || []).forEach((message, index) => {
|
|
41
|
+
errors.push({
|
|
42
|
+
message,
|
|
62
43
|
severity: SEVERITY.ERROR,
|
|
63
44
|
index,
|
|
64
45
|
source: 'liquid',
|
|
65
46
|
});
|
|
66
47
|
});
|
|
67
48
|
|
|
68
|
-
|
|
49
|
+
const warnings = (standardWarnings || []).map((message, index) => ({
|
|
50
|
+
message,
|
|
51
|
+
severity: SEVERITY.WARNING,
|
|
52
|
+
index,
|
|
53
|
+
source: 'standard',
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
return { errors, warnings };
|
|
69
57
|
};
|
|
70
58
|
|
|
71
59
|
/**
|
|
@@ -90,16 +78,6 @@ const cleanErrorMessage = (message, lineMatch, charMatch, ruleMatch) => {
|
|
|
90
78
|
return displayMessage.trim();
|
|
91
79
|
};
|
|
92
80
|
|
|
93
|
-
/**
|
|
94
|
-
* Get icon based on severity
|
|
95
|
-
*/
|
|
96
|
-
const getSeverityIcon = (severity) => {
|
|
97
|
-
if (severity === SEVERITY.WARNING) {
|
|
98
|
-
return <CapIcon type="alert-warning" className="error-info-note__icon error-info-note__icon--warning" />;
|
|
99
|
-
}
|
|
100
|
-
return <CapIcon type="warning-circle" className="error-info-note__icon error-info-note__icon--error" />;
|
|
101
|
-
};
|
|
102
|
-
|
|
103
81
|
/**
|
|
104
82
|
* Tab content component
|
|
105
83
|
*/
|
|
@@ -139,9 +117,6 @@ const TabContent = ({ issues, onErrorClick, intl }) => {
|
|
|
139
117
|
key={key}
|
|
140
118
|
className={`error-info-note__item error-info-note__item--${severity || 'error'}`}
|
|
141
119
|
>
|
|
142
|
-
<CapRow className="error-info-note__item-icon">
|
|
143
|
-
{getSeverityIcon(severity)}
|
|
144
|
-
</CapRow>
|
|
145
120
|
<CapRow className="error-info-note__item-content">
|
|
146
121
|
<CapLabelInline type="label2" className="error-info-note__item-message">
|
|
147
122
|
{displayMessage}
|
|
@@ -222,6 +197,7 @@ export const ErrorInfoNote = (props) => {
|
|
|
222
197
|
const {
|
|
223
198
|
LIQUID_ERROR_MSG: rawLiquidErrors = [],
|
|
224
199
|
STANDARD_ERROR_MSG: rawStandardErrors = [],
|
|
200
|
+
STANDARD_WARNING_MSG: rawStandardWarnings = [],
|
|
225
201
|
} = errorMessages || {};
|
|
226
202
|
|
|
227
203
|
// Detect if platform-specific (ANDROID/IOS) or GENERIC
|
|
@@ -280,33 +256,26 @@ export const ErrorInfoNote = (props) => {
|
|
|
280
256
|
);
|
|
281
257
|
}
|
|
282
258
|
|
|
283
|
-
//
|
|
284
|
-
const {
|
|
259
|
+
// Group into Errors (blocking) and Warnings (non-blocking)
|
|
260
|
+
const { errors: errorIssues, warnings: warningIssues } = useMemo(() => groupByErrorsAndWarnings(
|
|
285
261
|
Array.isArray(rawStandardErrors) ? rawStandardErrors : [],
|
|
286
262
|
Array.isArray(rawLiquidErrors) ? rawLiquidErrors : [],
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
const
|
|
291
|
-
const
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if (
|
|
300
|
-
|
|
301
|
-
setActiveKey(ERROR_TAB_KEYS.HTML);
|
|
302
|
-
} else if (labelCount > 0) {
|
|
303
|
-
setActiveKey(ERROR_TAB_KEYS.LABEL);
|
|
304
|
-
} else if (liquidCount > 0) {
|
|
305
|
-
// Show liquid tab even when liquid is disabled if liquid content is detected
|
|
306
|
-
setActiveKey(ERROR_TAB_KEYS.LIQUID);
|
|
307
|
-
}
|
|
263
|
+
Array.isArray(rawStandardWarnings) ? rawStandardWarnings : [],
|
|
264
|
+
), [rawStandardErrors, rawLiquidErrors, rawStandardWarnings]);
|
|
265
|
+
|
|
266
|
+
const errorsCount = errorIssues.length;
|
|
267
|
+
const warningsCount = warningIssues.length;
|
|
268
|
+
const totalCount = errorsCount + warningsCount;
|
|
269
|
+
const hasLiquidErrors = Array.isArray(rawLiquidErrors) && rawLiquidErrors.length > 0;
|
|
270
|
+
|
|
271
|
+
// Default active tab: errors if any, else warnings
|
|
272
|
+
useEffect(() => {
|
|
273
|
+
if (errorsCount > 0) {
|
|
274
|
+
setActiveKey(ERROR_TAB_KEYS.ERRORS);
|
|
275
|
+
} else if (warningsCount > 0) {
|
|
276
|
+
setActiveKey(ERROR_TAB_KEYS.WARNINGS);
|
|
308
277
|
}
|
|
309
|
-
}, [
|
|
278
|
+
}, [errorsCount, warningsCount]);
|
|
310
279
|
|
|
311
280
|
// Handle close
|
|
312
281
|
const handleClose = () => {
|
|
@@ -329,48 +298,25 @@ export const ErrorInfoNote = (props) => {
|
|
|
329
298
|
return null;
|
|
330
299
|
}
|
|
331
300
|
|
|
332
|
-
// Build tab panes
|
|
301
|
+
// Build tab panes: only Errors and Warnings
|
|
333
302
|
const tabPanes = [];
|
|
334
303
|
|
|
335
|
-
if (
|
|
336
|
-
tabPanes.push({
|
|
337
|
-
key: ERROR_TAB_KEYS.HTML,
|
|
338
|
-
tab: (
|
|
339
|
-
<CapLabelInline type="label2" className="error-info-note__tab-label">
|
|
340
|
-
<FormattedMessage {...messages.htmlIssues} />
|
|
341
|
-
<CapLabelInline type="label2" className="error-info-note__tab-count">
|
|
342
|
-
(
|
|
343
|
-
{htmlCount}
|
|
344
|
-
)
|
|
345
|
-
</CapLabelInline>
|
|
346
|
-
</CapLabelInline>
|
|
347
|
-
),
|
|
348
|
-
content: (
|
|
349
|
-
<TabContent
|
|
350
|
-
issues={htmlIssues}
|
|
351
|
-
onErrorClick={onErrorClick}
|
|
352
|
-
intl={intl}
|
|
353
|
-
/>
|
|
354
|
-
),
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
if (labelCount > 0) {
|
|
304
|
+
if (errorsCount > 0) {
|
|
359
305
|
tabPanes.push({
|
|
360
|
-
key: ERROR_TAB_KEYS.
|
|
306
|
+
key: ERROR_TAB_KEYS.ERRORS,
|
|
361
307
|
tab: (
|
|
362
|
-
<CapLabelInline type="label2" className="error-info-note__tab-label">
|
|
363
|
-
<FormattedMessage {...messages.
|
|
308
|
+
<CapLabelInline type="label2" className="error-info-note__tab-label error-info-note__tab-label--errors">
|
|
309
|
+
<FormattedMessage {...messages.errors} />
|
|
364
310
|
<CapLabelInline type="label2" className="error-info-note__tab-count">
|
|
365
311
|
(
|
|
366
|
-
{
|
|
312
|
+
{errorsCount}
|
|
367
313
|
)
|
|
368
314
|
</CapLabelInline>
|
|
369
315
|
</CapLabelInline>
|
|
370
316
|
),
|
|
371
317
|
content: (
|
|
372
318
|
<TabContent
|
|
373
|
-
issues={
|
|
319
|
+
issues={errorIssues}
|
|
374
320
|
onErrorClick={onErrorClick}
|
|
375
321
|
intl={intl}
|
|
376
322
|
/>
|
|
@@ -378,24 +324,22 @@ export const ErrorInfoNote = (props) => {
|
|
|
378
324
|
});
|
|
379
325
|
}
|
|
380
326
|
|
|
381
|
-
|
|
382
|
-
// This allows users to see errors when they add liquid content but liquid feature is not enabled
|
|
383
|
-
if (liquidCount > 0) {
|
|
327
|
+
if (warningsCount > 0) {
|
|
384
328
|
tabPanes.push({
|
|
385
|
-
key: ERROR_TAB_KEYS.
|
|
329
|
+
key: ERROR_TAB_KEYS.WARNINGS,
|
|
386
330
|
tab: (
|
|
387
|
-
<CapLabelInline type="label2" className="error-info-note__tab-label">
|
|
388
|
-
<FormattedMessage {...messages.
|
|
331
|
+
<CapLabelInline type="label2" className="error-info-note__tab-label error-info-note__tab-label--warnings">
|
|
332
|
+
<FormattedMessage {...messages.warnings} />
|
|
389
333
|
<CapLabelInline type="label2" className="error-info-note__tab-count">
|
|
390
334
|
(
|
|
391
|
-
{
|
|
335
|
+
{warningsCount}
|
|
392
336
|
)
|
|
393
337
|
</CapLabelInline>
|
|
394
338
|
</CapLabelInline>
|
|
395
339
|
),
|
|
396
340
|
content: (
|
|
397
341
|
<TabContent
|
|
398
|
-
issues={
|
|
342
|
+
issues={warningIssues}
|
|
399
343
|
onErrorClick={onErrorClick}
|
|
400
344
|
intl={intl}
|
|
401
345
|
/>
|
|
@@ -413,7 +357,7 @@ export const ErrorInfoNote = (props) => {
|
|
|
413
357
|
className="error-info-note__tabs"
|
|
414
358
|
/>
|
|
415
359
|
<CapRow className="error-info-note__actions">
|
|
416
|
-
{
|
|
360
|
+
{hasLiquidErrors && isLiquidEnabled && (
|
|
417
361
|
<CapButton
|
|
418
362
|
type="flat"
|
|
419
363
|
className="error-info-note__liquid-doc"
|
|
@@ -531,6 +475,7 @@ ErrorInfoNote.propTypes = {
|
|
|
531
475
|
GENERIC: PropTypes.array,
|
|
532
476
|
}),
|
|
533
477
|
]),
|
|
478
|
+
STANDARD_WARNING_MSG: PropTypes.array,
|
|
534
479
|
}),
|
|
535
480
|
onErrorClick: PropTypes.func,
|
|
536
481
|
onClose: PropTypes.func,
|
|
@@ -6,18 +6,14 @@
|
|
|
6
6
|
import { defineMessages } from "react-intl";
|
|
7
7
|
const scope = "creatives.componentsV2.ErrorInfoNote";
|
|
8
8
|
export default defineMessages({
|
|
9
|
-
// Tab labels
|
|
10
|
-
|
|
11
|
-
id: `${scope}.
|
|
12
|
-
defaultMessage: "
|
|
13
|
-
},
|
|
14
|
-
|
|
15
|
-
id: `${scope}.
|
|
16
|
-
defaultMessage: "
|
|
17
|
-
},
|
|
18
|
-
liquidIssues: {
|
|
19
|
-
id: `${scope}.liquidIssues`,
|
|
20
|
-
defaultMessage: "Liquid issues",
|
|
9
|
+
// Tab labels: Errors = blocking, Warnings = non-blocking
|
|
10
|
+
errors: {
|
|
11
|
+
id: `${scope}.errors`,
|
|
12
|
+
defaultMessage: "Errors",
|
|
13
|
+
},
|
|
14
|
+
warnings: {
|
|
15
|
+
id: `${scope}.warnings`,
|
|
16
|
+
defaultMessage: "Warnings",
|
|
21
17
|
},
|
|
22
18
|
navigateToError: {
|
|
23
19
|
id: `${scope}.navigateToError`,
|
|
@@ -41,8 +41,8 @@ import { useValidation } from './hooks/useValidation';
|
|
|
41
41
|
// Constants
|
|
42
42
|
import {
|
|
43
43
|
HTML_EDITOR_VARIANTS, DEVICE_TYPES, DEFAULT_HTML_CONTENT, TAG, EMBEDDED, DEFAULT, FULL, ALL, SMS, EMAIL,
|
|
44
|
+
BLOCKING_ERROR_RULE_IDS,
|
|
44
45
|
} from './constants';
|
|
45
|
-
import { ISSUE_SOURCES, LABEL_ISSUE_PATTERNS } from './utils/validationConstants';
|
|
46
46
|
|
|
47
47
|
// Styles
|
|
48
48
|
import './_htmlEditor.scss';
|
|
@@ -51,6 +51,30 @@ import './components/FullscreenModal/_fullscreenModal.scss';
|
|
|
51
51
|
// Messages
|
|
52
52
|
import messages from './messages';
|
|
53
53
|
|
|
54
|
+
/** Check if an issue is a blocking error (Errors tab). Non-blocking = Warnings. */
|
|
55
|
+
const isBlockingError = (issue) => {
|
|
56
|
+
const { rule, source, severity } = issue || {};
|
|
57
|
+
if (rule === 'liquid-api-validation' || rule === 'standard-api-validation') return true;
|
|
58
|
+
if (source === 'liquid-validator' && severity === 'error') return true;
|
|
59
|
+
if (BLOCKING_ERROR_RULE_IDS.includes(rule)) return true;
|
|
60
|
+
return false;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** Count issues as Errors (blocking) and Warnings (non-blocking). */
|
|
64
|
+
const countIssuesBySeverity = (allIssues) => {
|
|
65
|
+
let errors = 0;
|
|
66
|
+
let warnings = 0;
|
|
67
|
+
(allIssues || []).forEach((issue) => {
|
|
68
|
+
if (isBlockingError(issue)) errors += 1;
|
|
69
|
+
else warnings += 1;
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
errors,
|
|
73
|
+
warnings,
|
|
74
|
+
total: (allIssues || []).length,
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
|
|
54
78
|
const HTMLEditor = forwardRef(({
|
|
55
79
|
intl,
|
|
56
80
|
variant = HTML_EDITOR_VARIANTS.EMAIL, // New prop: 'email' or 'inapp'
|
|
@@ -276,96 +300,17 @@ const HTMLEditor = forwardRef(({
|
|
|
276
300
|
getContent: () => currentContent,
|
|
277
301
|
isContentEmpty: () => !currentContent || currentContent.trim() === '',
|
|
278
302
|
getIssueCounts: () => {
|
|
279
|
-
// Check if validation is ready and has getAllIssues method
|
|
280
303
|
if (!validation || typeof validation.getAllIssues !== 'function') {
|
|
281
|
-
return {
|
|
282
|
-
html: 0, label: 0, liquid: 0, total: 0,
|
|
283
|
-
};
|
|
304
|
+
return { errors: 0, warnings: 0, total: 0 };
|
|
284
305
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
let htmlCount = 0;
|
|
288
|
-
let labelCount = 0;
|
|
289
|
-
let liquidCount = 0;
|
|
290
|
-
|
|
291
|
-
allIssues.forEach((issue) => {
|
|
292
|
-
const { source, rule, message } = issue;
|
|
293
|
-
const messageLower = (message || '').toLowerCase();
|
|
294
|
-
const ruleLower = (rule || '').toLowerCase();
|
|
295
|
-
|
|
296
|
-
// Check if it's a Liquid issue
|
|
297
|
-
if (source === ISSUE_SOURCES.LIQUID) {
|
|
298
|
-
liquidCount++;
|
|
299
|
-
return;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Check if it's a Label (tag syntax) issue
|
|
303
|
-
const isLabelIssue = LABEL_ISSUE_PATTERNS.some(
|
|
304
|
-
(pattern) => messageLower.includes(pattern.toLowerCase())
|
|
305
|
-
|| ruleLower.includes(pattern.toLowerCase()),
|
|
306
|
-
);
|
|
307
|
-
|
|
308
|
-
if (isLabelIssue) {
|
|
309
|
-
labelCount++;
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Default to HTML issues
|
|
314
|
-
htmlCount++;
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
return {
|
|
318
|
-
html: htmlCount,
|
|
319
|
-
label: labelCount,
|
|
320
|
-
liquid: liquidCount,
|
|
321
|
-
total: allIssues.length,
|
|
322
|
-
};
|
|
306
|
+
return countIssuesBySeverity(validation.getAllIssues());
|
|
323
307
|
},
|
|
324
308
|
getValidationState: () => ({
|
|
325
309
|
isValidating: validation?.isValidating || false,
|
|
326
310
|
hasErrors: validation?.hasBlockingErrors || false,
|
|
327
|
-
issueCounts:
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
html: 0, label: 0, liquid: 0, total: 0,
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
const allIssues = validation.getAllIssues();
|
|
334
|
-
// Use same logic as getIssueCounts
|
|
335
|
-
let htmlCount = 0;
|
|
336
|
-
let labelCount = 0;
|
|
337
|
-
let liquidCount = 0;
|
|
338
|
-
|
|
339
|
-
allIssues.forEach((issue) => {
|
|
340
|
-
const { source, rule, message } = issue;
|
|
341
|
-
const messageLower = (message || '').toLowerCase();
|
|
342
|
-
const ruleLower = (rule || '').toLowerCase();
|
|
343
|
-
|
|
344
|
-
if (source === ISSUE_SOURCES.LIQUID) {
|
|
345
|
-
liquidCount++;
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
const isLabelIssue = LABEL_ISSUE_PATTERNS.some(
|
|
350
|
-
(pattern) => messageLower.includes(pattern.toLowerCase())
|
|
351
|
-
|| ruleLower.includes(pattern.toLowerCase()),
|
|
352
|
-
);
|
|
353
|
-
|
|
354
|
-
if (isLabelIssue) {
|
|
355
|
-
labelCount++;
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
htmlCount++;
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
return {
|
|
363
|
-
html: htmlCount,
|
|
364
|
-
label: labelCount,
|
|
365
|
-
liquid: liquidCount,
|
|
366
|
-
total: allIssues.length,
|
|
367
|
-
};
|
|
368
|
-
})(),
|
|
311
|
+
issueCounts: !validation || typeof validation.getAllIssues !== 'function'
|
|
312
|
+
? { errors: 0, warnings: 0, total: 0 }
|
|
313
|
+
: countIssuesBySeverity(validation.getAllIssues()),
|
|
369
314
|
})
|
|
370
315
|
,
|
|
371
316
|
}), [validation, currentContent, apiValidationErrors]); // Include apiValidationErrors so ref methods return updated counts
|
|
@@ -402,49 +347,13 @@ const HTMLEditor = forwardRef(({
|
|
|
402
347
|
return;
|
|
403
348
|
}
|
|
404
349
|
|
|
405
|
-
// Calculate issue counts
|
|
350
|
+
// Calculate issue counts: Errors (blocking) and Warnings (non-blocking)
|
|
406
351
|
const calculateIssueCounts = () => {
|
|
407
352
|
const currentValidation = validationRef.current;
|
|
408
353
|
if (!currentValidation || typeof currentValidation.getAllIssues !== 'function') {
|
|
409
|
-
return {
|
|
410
|
-
html: 0, label: 0, liquid: 0, total: 0,
|
|
411
|
-
};
|
|
354
|
+
return { errors: 0, warnings: 0, total: 0 };
|
|
412
355
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
let htmlCount = 0;
|
|
416
|
-
let labelCount = 0;
|
|
417
|
-
let liquidCount = 0;
|
|
418
|
-
|
|
419
|
-
allIssues.forEach((issue) => {
|
|
420
|
-
const { source, rule, message } = issue;
|
|
421
|
-
const messageLower = (message || '').toLowerCase();
|
|
422
|
-
const ruleLower = (rule || '').toLowerCase();
|
|
423
|
-
|
|
424
|
-
if (source === ISSUE_SOURCES.LIQUID) {
|
|
425
|
-
liquidCount += 1;
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
const isLabelIssue = LABEL_ISSUE_PATTERNS.some(
|
|
430
|
-
(pattern) => messageLower.includes(pattern.toLowerCase())
|
|
431
|
-
|| ruleLower.includes(pattern.toLowerCase()),
|
|
432
|
-
);
|
|
433
|
-
|
|
434
|
-
if (isLabelIssue) {
|
|
435
|
-
labelCount += 1;
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
htmlCount += 1;
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
return {
|
|
443
|
-
html: htmlCount,
|
|
444
|
-
label: labelCount,
|
|
445
|
-
liquid: liquidCount,
|
|
446
|
-
total: allIssues.length,
|
|
447
|
-
};
|
|
356
|
+
return countIssuesBySeverity(currentValidation.getAllIssues());
|
|
448
357
|
};
|
|
449
358
|
|
|
450
359
|
const issueCounts = calculateIssueCounts();
|
|
@@ -465,9 +374,8 @@ const HTMLEditor = forwardRef(({
|
|
|
465
374
|
|| lastState.validationComplete !== newState.validationComplete
|
|
466
375
|
|| lastState.hasErrors !== newState.hasErrors
|
|
467
376
|
|| lastState.issueCounts?.total !== newState.issueCounts?.total
|
|
468
|
-
|| lastState.issueCounts?.
|
|
469
|
-
|| lastState.issueCounts?.
|
|
470
|
-
|| lastState.issueCounts?.liquid !== newState.issueCounts?.liquid;
|
|
377
|
+
|| lastState.issueCounts?.errors !== newState.issueCounts?.errors
|
|
378
|
+
|| lastState.issueCounts?.warnings !== newState.issueCounts?.warnings;
|
|
471
379
|
|
|
472
380
|
if (hasChanged) {
|
|
473
381
|
lastSentValidationStateRef.current = newState;
|
|
@@ -487,9 +395,7 @@ const HTMLEditor = forwardRef(({
|
|
|
487
395
|
const isContentEmpty = !currentContent || currentContent.trim() === '';
|
|
488
396
|
onValidationChangeRef.current({
|
|
489
397
|
isContentEmpty,
|
|
490
|
-
issueCounts: {
|
|
491
|
-
html: 0, label: 0, liquid: 0, total: 0,
|
|
492
|
-
},
|
|
398
|
+
issueCounts: { errors: 0, warnings: 0, total: 0 },
|
|
493
399
|
validationComplete: false, // Validation hasn't run yet
|
|
494
400
|
hasErrors: false,
|
|
495
401
|
});
|
|
@@ -507,51 +413,10 @@ const HTMLEditor = forwardRef(({
|
|
|
507
413
|
return;
|
|
508
414
|
}
|
|
509
415
|
|
|
510
|
-
// Recalculate issue counts including API errors
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
html: 0, label: 0, liquid: 0, total: 0,
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
const allIssues = validation.getAllIssues();
|
|
518
|
-
|
|
519
|
-
let htmlCount = 0;
|
|
520
|
-
let labelCount = 0;
|
|
521
|
-
let liquidCount = 0;
|
|
522
|
-
|
|
523
|
-
allIssues.forEach((issue) => {
|
|
524
|
-
const { source, rule, message } = issue;
|
|
525
|
-
const messageLower = (message || '').toLowerCase();
|
|
526
|
-
const ruleLower = (rule || '').toLowerCase();
|
|
527
|
-
|
|
528
|
-
if (source === ISSUE_SOURCES.LIQUID) {
|
|
529
|
-
liquidCount += 1;
|
|
530
|
-
return;
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
const isLabelIssue = LABEL_ISSUE_PATTERNS.some(
|
|
534
|
-
(pattern) => messageLower.includes(pattern.toLowerCase())
|
|
535
|
-
|| ruleLower.includes(pattern.toLowerCase()),
|
|
536
|
-
);
|
|
537
|
-
|
|
538
|
-
if (isLabelIssue) {
|
|
539
|
-
labelCount += 1;
|
|
540
|
-
return;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
htmlCount += 1;
|
|
544
|
-
});
|
|
545
|
-
|
|
546
|
-
return {
|
|
547
|
-
html: htmlCount,
|
|
548
|
-
label: labelCount,
|
|
549
|
-
liquid: liquidCount,
|
|
550
|
-
total: allIssues.length,
|
|
551
|
-
};
|
|
552
|
-
};
|
|
553
|
-
|
|
554
|
-
const issueCounts = calculateIssueCounts();
|
|
416
|
+
// Recalculate issue counts (Errors + Warnings) including API errors
|
|
417
|
+
const issueCounts = !validation || typeof validation.getAllIssues !== 'function'
|
|
418
|
+
? { errors: 0, warnings: 0, total: 0 }
|
|
419
|
+
: countIssuesBySeverity(validation.getAllIssues());
|
|
555
420
|
const isContentEmpty = !currentContent || currentContent.trim() === '';
|
|
556
421
|
|
|
557
422
|
const newState = {
|
|
@@ -565,9 +430,8 @@ const HTMLEditor = forwardRef(({
|
|
|
565
430
|
const hasChanged = !lastState
|
|
566
431
|
|| lastState.hasErrors !== newState.hasErrors
|
|
567
432
|
|| lastState.issueCounts?.total !== newState.issueCounts?.total
|
|
568
|
-
|| lastState.issueCounts?.
|
|
569
|
-
|| lastState.issueCounts?.
|
|
570
|
-
|| lastState.issueCounts?.liquid !== newState.issueCounts?.liquid;
|
|
433
|
+
|| lastState.issueCounts?.errors !== newState.issueCounts?.errors
|
|
434
|
+
|| lastState.issueCounts?.warnings !== newState.issueCounts?.warnings;
|
|
571
435
|
|
|
572
436
|
if (hasChanged) {
|
|
573
437
|
lastSentValidationStateRef.current = newState;
|
|
@@ -643,10 +507,10 @@ const HTMLEditor = forwardRef(({
|
|
|
643
507
|
const handleSave = useCallback(() => {
|
|
644
508
|
try {
|
|
645
509
|
const { html, css, javascript } = content?.content || {};
|
|
646
|
-
const
|
|
510
|
+
const contentToSave = { html, css, javascript };
|
|
647
511
|
|
|
648
512
|
if (onSave) {
|
|
649
|
-
onSave(
|
|
513
|
+
onSave(contentToSave);
|
|
650
514
|
}
|
|
651
515
|
|
|
652
516
|
markAsSaved?.();
|
|
@@ -819,10 +683,10 @@ const HTMLEditor = forwardRef(({
|
|
|
819
683
|
visible={isFullscreenModalOpen}
|
|
820
684
|
onCancel={handleCloseFullscreen}
|
|
821
685
|
footer={null}
|
|
822
|
-
maskClosable
|
|
686
|
+
maskClosable
|
|
823
687
|
centered
|
|
824
688
|
closable={false}
|
|
825
|
-
width="
|
|
689
|
+
width="93vw"
|
|
826
690
|
className="html-editor-fullscreen-modal"
|
|
827
691
|
>
|
|
828
692
|
<CapRow className={`html-editor-fullscreen html-editor-fullscreen--${variant} ${isLibraryMode ? 'html-editor-fullscreen--library-mode' : ''}`}>
|