@capillarytech/creatives-library 8.0.262-alpha.1 → 8.0.263
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 +58 -113
- package/v2Components/ErrorInfoNote/messages.js +8 -12
- package/v2Components/HtmlEditor/HTMLEditor.js +48 -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/EditorToolbar/_editorToolbar.scss +0 -1
- package/v2Components/HtmlEditor/components/EditorToolbar/index.js +27 -2
- 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/index.js +10 -33
- package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +48 -13
- package/v2Components/HtmlEditor/components/ValidationTabs/index.js +77 -146
- package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +14 -14
- package/v2Components/HtmlEditor/constants.js +3 -0
- package/v2Components/HtmlEditor/hooks/useValidation.js +34 -13
- package/v2Components/HtmlEditor/messages.js +10 -0
- package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +38 -36
- package/v2Components/HtmlEditor/utils/validationConstants.js +3 -4
- package/v2Components/MobilePushPreviewV2/constants.js +6 -0
- package/v2Components/MobilePushPreviewV2/index.js +4 -3
- 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="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"
|
|
@@ -462,7 +406,7 @@ const ErrorSection = ({
|
|
|
462
406
|
<CapButton
|
|
463
407
|
type="flat"
|
|
464
408
|
className="add-btn"
|
|
465
|
-
onClick={() => window.open(
|
|
409
|
+
onClick={() => window.open(LIQUID_DOC_URL, '_blank')}
|
|
466
410
|
>
|
|
467
411
|
<FormattedMessage {...messages.liquidDoc} />
|
|
468
412
|
<CapIcon size="s" type="launch" />
|
|
@@ -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,9 @@ 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,
|
|
45
|
+
VALIDATION_SEVERITY,
|
|
44
46
|
} from './constants';
|
|
45
|
-
import { ISSUE_SOURCES, LABEL_ISSUE_PATTERNS } from './utils/validationConstants';
|
|
46
47
|
|
|
47
48
|
// Styles
|
|
48
49
|
import './_htmlEditor.scss';
|
|
@@ -50,6 +51,31 @@ import './components/FullscreenModal/_fullscreenModal.scss';
|
|
|
50
51
|
|
|
51
52
|
// Messages
|
|
52
53
|
import messages from './messages';
|
|
54
|
+
import { ISSUE_SOURCES } from './utils/validationConstants';
|
|
55
|
+
|
|
56
|
+
/** Check if an issue is a blocking error (Errors tab). Non-blocking = Warnings. */
|
|
57
|
+
const isBlockingError = (issue) => {
|
|
58
|
+
const { rule, source, severity } = issue || {};
|
|
59
|
+
if (rule === 'liquid-api-validation' || rule === 'standard-api-validation') return true;
|
|
60
|
+
if (source === ISSUE_SOURCES.LIQUID && severity === VALIDATION_SEVERITY.ERROR) return true;
|
|
61
|
+
if (BLOCKING_ERROR_RULE_IDS.includes(rule)) return true;
|
|
62
|
+
return false;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/** Count issues as Errors (blocking) and Warnings (non-blocking). */
|
|
66
|
+
const countIssuesBySeverity = (allIssues) => {
|
|
67
|
+
let errors = 0;
|
|
68
|
+
let warnings = 0;
|
|
69
|
+
(allIssues || []).forEach((issue) => {
|
|
70
|
+
if (isBlockingError(issue)) errors += 1;
|
|
71
|
+
else warnings += 1;
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
errors,
|
|
75
|
+
warnings,
|
|
76
|
+
total: (allIssues || []).length,
|
|
77
|
+
};
|
|
78
|
+
};
|
|
53
79
|
|
|
54
80
|
const HTMLEditor = forwardRef(({
|
|
55
81
|
intl,
|
|
@@ -276,96 +302,17 @@ const HTMLEditor = forwardRef(({
|
|
|
276
302
|
getContent: () => currentContent,
|
|
277
303
|
isContentEmpty: () => !currentContent || currentContent.trim() === '',
|
|
278
304
|
getIssueCounts: () => {
|
|
279
|
-
// Check if validation is ready and has getAllIssues method
|
|
280
305
|
if (!validation || typeof validation.getAllIssues !== 'function') {
|
|
281
|
-
return {
|
|
282
|
-
html: 0, label: 0, liquid: 0, total: 0,
|
|
283
|
-
};
|
|
306
|
+
return { errors: 0, warnings: 0, total: 0 };
|
|
284
307
|
}
|
|
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
|
-
};
|
|
308
|
+
return countIssuesBySeverity(validation.getAllIssues());
|
|
323
309
|
},
|
|
324
310
|
getValidationState: () => ({
|
|
325
311
|
isValidating: validation?.isValidating || false,
|
|
326
312
|
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
|
-
})(),
|
|
313
|
+
issueCounts: !validation || typeof validation.getAllIssues !== 'function'
|
|
314
|
+
? { errors: 0, warnings: 0, total: 0 }
|
|
315
|
+
: countIssuesBySeverity(validation.getAllIssues()),
|
|
369
316
|
})
|
|
370
317
|
,
|
|
371
318
|
}), [validation, currentContent, apiValidationErrors]); // Include apiValidationErrors so ref methods return updated counts
|
|
@@ -402,49 +349,13 @@ const HTMLEditor = forwardRef(({
|
|
|
402
349
|
return;
|
|
403
350
|
}
|
|
404
351
|
|
|
405
|
-
// Calculate issue counts
|
|
352
|
+
// Calculate issue counts: Errors (blocking) and Warnings (non-blocking)
|
|
406
353
|
const calculateIssueCounts = () => {
|
|
407
354
|
const currentValidation = validationRef.current;
|
|
408
355
|
if (!currentValidation || typeof currentValidation.getAllIssues !== 'function') {
|
|
409
|
-
return {
|
|
410
|
-
html: 0, label: 0, liquid: 0, total: 0,
|
|
411
|
-
};
|
|
356
|
+
return { errors: 0, warnings: 0, total: 0 };
|
|
412
357
|
}
|
|
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
|
-
};
|
|
358
|
+
return countIssuesBySeverity(currentValidation.getAllIssues());
|
|
448
359
|
};
|
|
449
360
|
|
|
450
361
|
const issueCounts = calculateIssueCounts();
|
|
@@ -465,9 +376,8 @@ const HTMLEditor = forwardRef(({
|
|
|
465
376
|
|| lastState.validationComplete !== newState.validationComplete
|
|
466
377
|
|| lastState.hasErrors !== newState.hasErrors
|
|
467
378
|
|| lastState.issueCounts?.total !== newState.issueCounts?.total
|
|
468
|
-
|| lastState.issueCounts?.
|
|
469
|
-
|| lastState.issueCounts?.
|
|
470
|
-
|| lastState.issueCounts?.liquid !== newState.issueCounts?.liquid;
|
|
379
|
+
|| lastState.issueCounts?.errors !== newState.issueCounts?.errors
|
|
380
|
+
|| lastState.issueCounts?.warnings !== newState.issueCounts?.warnings;
|
|
471
381
|
|
|
472
382
|
if (hasChanged) {
|
|
473
383
|
lastSentValidationStateRef.current = newState;
|
|
@@ -487,9 +397,7 @@ const HTMLEditor = forwardRef(({
|
|
|
487
397
|
const isContentEmpty = !currentContent || currentContent.trim() === '';
|
|
488
398
|
onValidationChangeRef.current({
|
|
489
399
|
isContentEmpty,
|
|
490
|
-
issueCounts: {
|
|
491
|
-
html: 0, label: 0, liquid: 0, total: 0,
|
|
492
|
-
},
|
|
400
|
+
issueCounts: { errors: 0, warnings: 0, total: 0 },
|
|
493
401
|
validationComplete: false, // Validation hasn't run yet
|
|
494
402
|
hasErrors: false,
|
|
495
403
|
});
|
|
@@ -507,51 +415,10 @@ const HTMLEditor = forwardRef(({
|
|
|
507
415
|
return;
|
|
508
416
|
}
|
|
509
417
|
|
|
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();
|
|
418
|
+
// Recalculate issue counts (Errors + Warnings) including API errors
|
|
419
|
+
const issueCounts = !validation || typeof validation.getAllIssues !== 'function'
|
|
420
|
+
? { errors: 0, warnings: 0, total: 0 }
|
|
421
|
+
: countIssuesBySeverity(validation.getAllIssues());
|
|
555
422
|
const isContentEmpty = !currentContent || currentContent.trim() === '';
|
|
556
423
|
|
|
557
424
|
const newState = {
|
|
@@ -565,9 +432,8 @@ const HTMLEditor = forwardRef(({
|
|
|
565
432
|
const hasChanged = !lastState
|
|
566
433
|
|| lastState.hasErrors !== newState.hasErrors
|
|
567
434
|
|| lastState.issueCounts?.total !== newState.issueCounts?.total
|
|
568
|
-
|| lastState.issueCounts?.
|
|
569
|
-
|| lastState.issueCounts?.
|
|
570
|
-
|| lastState.issueCounts?.liquid !== newState.issueCounts?.liquid;
|
|
435
|
+
|| lastState.issueCounts?.errors !== newState.issueCounts?.errors
|
|
436
|
+
|| lastState.issueCounts?.warnings !== newState.issueCounts?.warnings;
|
|
571
437
|
|
|
572
438
|
if (hasChanged) {
|
|
573
439
|
lastSentValidationStateRef.current = newState;
|
|
@@ -643,10 +509,10 @@ const HTMLEditor = forwardRef(({
|
|
|
643
509
|
const handleSave = useCallback(() => {
|
|
644
510
|
try {
|
|
645
511
|
const { html, css, javascript } = content?.content || {};
|
|
646
|
-
const
|
|
512
|
+
const contentToSave = { html, css, javascript };
|
|
647
513
|
|
|
648
514
|
if (onSave) {
|
|
649
|
-
onSave(
|
|
515
|
+
onSave(contentToSave);
|
|
650
516
|
}
|
|
651
517
|
|
|
652
518
|
markAsSaved?.();
|
|
@@ -819,10 +685,10 @@ const HTMLEditor = forwardRef(({
|
|
|
819
685
|
visible={isFullscreenModalOpen}
|
|
820
686
|
onCancel={handleCloseFullscreen}
|
|
821
687
|
footer={null}
|
|
822
|
-
maskClosable
|
|
688
|
+
maskClosable
|
|
823
689
|
centered
|
|
824
690
|
closable={false}
|
|
825
|
-
width="
|
|
691
|
+
width="93vw"
|
|
826
692
|
className="html-editor-fullscreen-modal"
|
|
827
693
|
>
|
|
828
694
|
<CapRow className={`html-editor-fullscreen html-editor-fullscreen--${variant} ${isLibraryMode ? 'html-editor-fullscreen--library-mode' : ''}`}>
|