@pixelated-tech/components 3.5.12 → 3.5.14
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/dist/components/admin/site-health/seo-metrics.config.json +38 -18
- package/dist/components/admin/site-health/site-health-accessibility.js +2 -94
- package/dist/components/admin/site-health/site-health-axe-core.js +2 -2
- package/dist/components/admin/site-health/site-health-cloudwatch.js +2 -2
- package/dist/components/admin/site-health/site-health-dependency-vulnerabilities.js +1 -1
- package/dist/components/admin/site-health/site-health-github.js +3 -3
- package/dist/components/admin/site-health/site-health-google-analytics.js +1 -1
- package/dist/components/admin/site-health/site-health-google-search-console.js +1 -1
- package/dist/components/admin/site-health/site-health-on-site-seo.integration.js +275 -3
- package/dist/components/admin/site-health/site-health-on-site-seo.js +5 -56
- package/dist/components/admin/site-health/site-health-overview.js +1 -9
- package/dist/components/admin/site-health/site-health-performance.js +2 -158
- package/dist/components/admin/site-health/site-health-security.js +2 -139
- package/dist/components/admin/site-health/site-health-seo.js +2 -94
- package/dist/components/admin/site-health/site-health-utils.js +170 -0
- package/dist/components/admin/site-health/site-health.css +33 -3
- package/dist/components/cms/smartimage.js +28 -8
- package/dist/components/general/table.js +2 -2
- package/dist/components/sitebuilder/config/ConfigBuilder.js +12 -3
- package/dist/index.adminclient.js +1 -0
- package/dist/index.adminserver.js +1 -0
- package/dist/types/components/admin/site-health/site-health-accessibility.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-on-site-seo.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-on-site-seo.integration.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-overview.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-performance.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-security.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-seo.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-utils.d.ts +17 -0
- package/dist/types/components/admin/site-health/site-health-utils.d.ts.map +1 -0
- package/dist/types/components/cms/smartimage.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/config/ConfigBuilder.d.ts.map +1 -1
- package/dist/types/index.adminclient.d.ts +1 -0
- package/dist/types/index.adminserver.d.ts +1 -0
- package/dist/types/tests/test-seo-logic.d.ts +2 -0
- package/dist/types/tests/test-seo-logic.d.ts.map +1 -0
- package/package.json +4 -4
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import { useCallback } from 'react';
|
|
4
4
|
import PropTypes from 'prop-types';
|
|
5
5
|
import { SiteHealthTemplate } from './site-health-template';
|
|
6
|
-
import {
|
|
6
|
+
import { formatAuditItem, getAuditScoreIcon, getScoreColor } from './site-health-utils';
|
|
7
7
|
SiteHealthSecurity.propTypes = {
|
|
8
8
|
siteName: PropTypes.string.isRequired,
|
|
9
9
|
};
|
|
@@ -17,143 +17,6 @@ export function SiteHealthSecurity({ siteName }) {
|
|
|
17
17
|
};
|
|
18
18
|
}, []);
|
|
19
19
|
return (_jsx(SiteHealthTemplate, { siteName: siteName, title: "PageSpeed - Site Security", fetchData: fetchSecurityData, children: (data) => {
|
|
20
|
-
const getScoreColor = (score) => {
|
|
21
|
-
return getScoreIndicator(score).color;
|
|
22
|
-
};
|
|
23
|
-
const getAuditScoreIcon = (score) => {
|
|
24
|
-
return getScoreIndicator(score).icon;
|
|
25
|
-
};
|
|
26
|
-
// Helper function to display audit item details
|
|
27
|
-
const formatAuditItem = (item, auditTitle) => {
|
|
28
|
-
// Handle URLs
|
|
29
|
-
if (item.url && typeof item.url === 'string') {
|
|
30
|
-
return item.url;
|
|
31
|
-
}
|
|
32
|
-
// Handle sources (like JavaScript files)
|
|
33
|
-
if (item.source && typeof item.source === 'string') {
|
|
34
|
-
return item.source;
|
|
35
|
-
}
|
|
36
|
-
// Handle text descriptions
|
|
37
|
-
if (item.text && typeof item.text === 'string') {
|
|
38
|
-
return item.text;
|
|
39
|
-
}
|
|
40
|
-
// Handle entities (like "Google Tag Manager")
|
|
41
|
-
if (item.entity && typeof item.entity === 'string') {
|
|
42
|
-
return item.entity;
|
|
43
|
-
}
|
|
44
|
-
// Handle nodes with selectors
|
|
45
|
-
if (item.node && typeof item.node === 'object' && 'selector' in item.node) {
|
|
46
|
-
return `Element: ${item.node.selector}`;
|
|
47
|
-
}
|
|
48
|
-
// Handle nodes with snippets
|
|
49
|
-
if (item.node && typeof item.node === 'object' && 'snippet' in item.node) {
|
|
50
|
-
const snippet = item.node.snippet;
|
|
51
|
-
return `Element: ${snippet.length > 50 ? snippet.substring(0, 50) + '...' : snippet}`;
|
|
52
|
-
}
|
|
53
|
-
// Handle origins (like domains)
|
|
54
|
-
if (item.origin && typeof item.origin === 'string') {
|
|
55
|
-
return item.origin;
|
|
56
|
-
}
|
|
57
|
-
// Handle labels
|
|
58
|
-
if (item.label && typeof item.label === 'string') {
|
|
59
|
-
return item.label;
|
|
60
|
-
}
|
|
61
|
-
// Handle numeric values with units
|
|
62
|
-
if (item.value && typeof item.value === 'object' && 'type' in item.value && item.value.type === 'numeric') {
|
|
63
|
-
const value = item.value;
|
|
64
|
-
return `${value.value}${item.unit || ''}`;
|
|
65
|
-
}
|
|
66
|
-
// Handle statistics
|
|
67
|
-
if (item.statistic && typeof item.statistic === 'string' && item.value) {
|
|
68
|
-
if (typeof item.value === 'object' && 'type' in item.value && item.value.type === 'numeric') {
|
|
69
|
-
const value = item.value;
|
|
70
|
-
return `${item.statistic}: ${value.value}`;
|
|
71
|
-
}
|
|
72
|
-
return item.statistic;
|
|
73
|
-
}
|
|
74
|
-
// Handle timing data with more context
|
|
75
|
-
if (item.duration && typeof item.duration === 'number') {
|
|
76
|
-
const duration = item.duration;
|
|
77
|
-
let context = '';
|
|
78
|
-
if (item.url && typeof item.url === 'string') {
|
|
79
|
-
context = ` for ${item.url}`;
|
|
80
|
-
}
|
|
81
|
-
else if (item.source && typeof item.source === 'string') {
|
|
82
|
-
context = ` for ${item.source}`;
|
|
83
|
-
}
|
|
84
|
-
else if (item.name && typeof item.name === 'string') {
|
|
85
|
-
context = ` for ${item.name}`;
|
|
86
|
-
}
|
|
87
|
-
return `${duration.toFixed(2)}ms${context}`;
|
|
88
|
-
}
|
|
89
|
-
// Handle response times
|
|
90
|
-
if (item.responseTime && typeof item.responseTime === 'number') {
|
|
91
|
-
const url = (item.url && typeof item.url === 'string') ? ` (${item.url})` : '';
|
|
92
|
-
return `${item.responseTime.toFixed(2)}ms response time${url}`;
|
|
93
|
-
}
|
|
94
|
-
// Handle start/end times
|
|
95
|
-
if ((item.startTime || item.endTime) && typeof (item.startTime || item.endTime) === 'number') {
|
|
96
|
-
const start = item.startTime && typeof item.startTime === 'number' ? item.startTime.toFixed(2) : '?';
|
|
97
|
-
const end = item.endTime && typeof item.endTime === 'number' ? item.endTime.toFixed(2) : '?';
|
|
98
|
-
const url = (item.url && typeof item.url === 'string') ? ` for ${item.url}` : '';
|
|
99
|
-
return `${start}ms - ${end}ms${url}`;
|
|
100
|
-
}
|
|
101
|
-
// Handle transfer size with timing
|
|
102
|
-
if (item.transferSize && typeof item.transferSize === 'number' && item.duration && typeof item.duration === 'number') {
|
|
103
|
-
const size = (item.transferSize / 1024).toFixed(1);
|
|
104
|
-
const time = item.duration.toFixed(2);
|
|
105
|
-
const url = (item.url && typeof item.url === 'string') ? ` (${item.url})` : '';
|
|
106
|
-
return `${size} KB in ${time}ms${url}`;
|
|
107
|
-
}
|
|
108
|
-
// Handle main thread time
|
|
109
|
-
if (item.mainThreadTime && typeof item.mainThreadTime === 'number') {
|
|
110
|
-
return `${item.mainThreadTime.toFixed(1)}ms`;
|
|
111
|
-
}
|
|
112
|
-
// For other objects, try to find a meaningful display
|
|
113
|
-
if (item.group && typeof item.group === 'string') {
|
|
114
|
-
return item.group;
|
|
115
|
-
}
|
|
116
|
-
if (item.type && typeof item.type === 'string') {
|
|
117
|
-
return item.type;
|
|
118
|
-
}
|
|
119
|
-
// If we can't find anything meaningful, provide a generic description
|
|
120
|
-
// This handles raw timing data that might be from various performance metrics
|
|
121
|
-
if (typeof item === 'number') {
|
|
122
|
-
let context = '';
|
|
123
|
-
if (auditTitle) {
|
|
124
|
-
if (auditTitle.toLowerCase().includes('server') || auditTitle.toLowerCase().includes('backend')) {
|
|
125
|
-
context = ' server response';
|
|
126
|
-
}
|
|
127
|
-
else if (auditTitle.toLowerCase().includes('network') || auditTitle.toLowerCase().includes('request')) {
|
|
128
|
-
context = ' network request';
|
|
129
|
-
}
|
|
130
|
-
else if (auditTitle.toLowerCase().includes('render') || auditTitle.toLowerCase().includes('blocking')) {
|
|
131
|
-
context = ' render blocking';
|
|
132
|
-
}
|
|
133
|
-
else if (auditTitle.toLowerCase().includes('javascript') || auditTitle.toLowerCase().includes('js')) {
|
|
134
|
-
context = ' JavaScript';
|
|
135
|
-
}
|
|
136
|
-
else if (auditTitle.toLowerCase().includes('image') || auditTitle.toLowerCase().includes('media')) {
|
|
137
|
-
context = ' media resource';
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return `${item.toFixed(2)}ms${context}`;
|
|
141
|
-
}
|
|
142
|
-
if (item.value && typeof item.value === 'number') {
|
|
143
|
-
const unit = item.unit || 'ms';
|
|
144
|
-
let context = '';
|
|
145
|
-
if (auditTitle && unit === 'ms') {
|
|
146
|
-
if (auditTitle.toLowerCase().includes('server')) {
|
|
147
|
-
context = ' server time';
|
|
148
|
-
}
|
|
149
|
-
else if (auditTitle.toLowerCase().includes('network')) {
|
|
150
|
-
context = ' network time';
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
return `${item.value.toFixed(2)}${unit}${context}`;
|
|
154
|
-
}
|
|
155
|
-
return 'Details available';
|
|
156
|
-
};
|
|
157
20
|
const psiData = data?.psiData?.data?.[0];
|
|
158
21
|
if (!psiData) {
|
|
159
22
|
return (_jsx("p", { style: { color: '#6b7280' }, children: "No security data available for this site." }));
|
|
@@ -164,7 +27,7 @@ export function SiteHealthSecurity({ siteName }) {
|
|
|
164
27
|
return (_jsxs(_Fragment, { children: [_jsx("h4", { className: "health-site-name", children: siteName.replace('-', ' ') }), _jsxs("p", { className: "health-site-url", children: ["URL: ", psiData.url] }), psiData.scores['best-practices'] !== null && (_jsx("div", { className: "health-score-container", children: _jsxs("div", { className: "health-score-item", children: [_jsx("div", { className: "health-score-label", children: "Best Practices Score" }), _jsxs("div", { className: "health-score-value", style: { color: getScoreColor(psiData.scores['best-practices']) }, children: [Math.round((psiData.scores['best-practices'] || 0) * 100), "%"] }), _jsx("div", { className: "health-score-bar", children: _jsx("div", { className: "health-score-fill", style: {
|
|
165
28
|
width: `${(psiData.scores['best-practices'] || 0) * 100}%`,
|
|
166
29
|
backgroundColor: getScoreColor(psiData.scores['best-practices'])
|
|
167
|
-
} }) })] }) })), psiData.categories['best-practices'] && psiData.categories['best-practices'].audits.length > 0 && (_jsxs("div", { children: [_jsx("h5", { style: { fontSize: '1rem', fontWeight: '600', marginBottom: '1rem' }, children: "Security Best Practices" }), _jsx("div", { className: "
|
|
30
|
+
} }) })] }) })), psiData.categories['best-practices'] && psiData.categories['best-practices'].audits.length > 0 && (_jsxs("div", { children: [_jsx("h5", { style: { fontSize: '1rem', fontWeight: '600', marginBottom: '1rem' }, children: "Security Best Practices" }), _jsx("div", { className: "health-audit-list", children: psiData.categories['best-practices'].audits
|
|
168
31
|
.filter((audit) => audit.scoreDisplayMode !== 'notApplicable')
|
|
169
32
|
.sort((a, b) => (b.score || 0) - (a.score || 0))
|
|
170
33
|
.slice(0, 20)
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import { useCallback } from 'react';
|
|
4
4
|
import PropTypes from 'prop-types';
|
|
5
5
|
import { SiteHealthTemplate } from './site-health-template';
|
|
6
|
-
import {
|
|
6
|
+
import { formatAuditItem, getAuditScoreIcon, getScoreColor } from './site-health-utils';
|
|
7
7
|
SiteHealthSEO.propTypes = {
|
|
8
8
|
siteName: PropTypes.string.isRequired,
|
|
9
9
|
};
|
|
@@ -17,98 +17,6 @@ export function SiteHealthSEO({ siteName }) {
|
|
|
17
17
|
return result;
|
|
18
18
|
}, []);
|
|
19
19
|
return (_jsx(SiteHealthTemplate, { siteName: siteName, title: "PageSpeed - SEO", fetchData: fetchSEOData, children: (data) => {
|
|
20
|
-
const getScoreColor = (score) => {
|
|
21
|
-
return getScoreIndicator(score).color;
|
|
22
|
-
};
|
|
23
|
-
const getAuditScoreIcon = (score) => {
|
|
24
|
-
return getScoreIndicator(score).icon;
|
|
25
|
-
};
|
|
26
|
-
// Helper function to display audit item details
|
|
27
|
-
const formatAuditItem = (item, auditTitle) => {
|
|
28
|
-
// Handle URLs
|
|
29
|
-
if (item.url && typeof item.url === 'string') {
|
|
30
|
-
return item.url;
|
|
31
|
-
}
|
|
32
|
-
// Handle sources (like JavaScript files)
|
|
33
|
-
if (item.source && typeof item.source === 'string') {
|
|
34
|
-
return item.source;
|
|
35
|
-
}
|
|
36
|
-
// Handle text descriptions
|
|
37
|
-
if (item.text && typeof item.text === 'string') {
|
|
38
|
-
return item.text;
|
|
39
|
-
}
|
|
40
|
-
// Handle entities (like "Google Tag Manager")
|
|
41
|
-
if (item.entity && typeof item.entity === 'string') {
|
|
42
|
-
return item.entity;
|
|
43
|
-
}
|
|
44
|
-
// Handle nodes with selectors
|
|
45
|
-
if (item.node && typeof item.node === 'object' && 'selector' in item.node) {
|
|
46
|
-
return `Element: ${item.node.selector}`;
|
|
47
|
-
}
|
|
48
|
-
// Handle nodes with snippets
|
|
49
|
-
if (item.node && typeof item.node === 'object' && 'snippet' in item.node) {
|
|
50
|
-
const snippet = item.node.snippet;
|
|
51
|
-
return `Element: ${snippet.length > 50 ? snippet.substring(0, 50) + '...' : snippet}`;
|
|
52
|
-
}
|
|
53
|
-
// Handle origins (like domains)
|
|
54
|
-
if (item.origin && typeof item.origin === 'string') {
|
|
55
|
-
return item.origin;
|
|
56
|
-
}
|
|
57
|
-
// Handle labels
|
|
58
|
-
if (item.label && typeof item.label === 'string') {
|
|
59
|
-
return item.label;
|
|
60
|
-
}
|
|
61
|
-
// Handle numeric values with units
|
|
62
|
-
if (item.value && typeof item.value === 'object' && 'type' in item.value && item.value.type === 'numeric') {
|
|
63
|
-
const value = item.value;
|
|
64
|
-
return `${value.value}${item.unit || ''}`;
|
|
65
|
-
}
|
|
66
|
-
// Handle statistics
|
|
67
|
-
if (item.statistic && typeof item.statistic === 'string' && item.value) {
|
|
68
|
-
if (typeof item.value === 'object' && 'type' in item.value && item.value.type === 'numeric') {
|
|
69
|
-
const value = item.value;
|
|
70
|
-
return `${item.statistic}: ${value.value}`;
|
|
71
|
-
}
|
|
72
|
-
return item.statistic;
|
|
73
|
-
}
|
|
74
|
-
// Handle timing data with audit context
|
|
75
|
-
if (typeof item === 'number') {
|
|
76
|
-
let context = '';
|
|
77
|
-
if (auditTitle) {
|
|
78
|
-
if (auditTitle.toLowerCase().includes('server') || auditTitle.toLowerCase().includes('backend')) {
|
|
79
|
-
context = ' server response';
|
|
80
|
-
}
|
|
81
|
-
else if (auditTitle.toLowerCase().includes('network') || auditTitle.toLowerCase().includes('request')) {
|
|
82
|
-
context = ' network request';
|
|
83
|
-
}
|
|
84
|
-
else if (auditTitle.toLowerCase().includes('render') || auditTitle.toLowerCase().includes('blocking')) {
|
|
85
|
-
context = ' render blocking';
|
|
86
|
-
}
|
|
87
|
-
else if (auditTitle.toLowerCase().includes('javascript') || auditTitle.toLowerCase().includes('js')) {
|
|
88
|
-
context = ' JavaScript';
|
|
89
|
-
}
|
|
90
|
-
else if (auditTitle.toLowerCase().includes('image') || auditTitle.toLowerCase().includes('media')) {
|
|
91
|
-
context = ' media resource';
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return `${item.toFixed(2)}ms${context}`;
|
|
95
|
-
}
|
|
96
|
-
if (item.value && typeof item.value === 'number') {
|
|
97
|
-
const unit = item.unit || 'ms';
|
|
98
|
-
let context = '';
|
|
99
|
-
if (auditTitle && unit === 'ms') {
|
|
100
|
-
if (auditTitle.toLowerCase().includes('server')) {
|
|
101
|
-
context = ' server time';
|
|
102
|
-
}
|
|
103
|
-
else if (auditTitle.toLowerCase().includes('network')) {
|
|
104
|
-
context = ' network time';
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return `${item.value.toFixed(2)}${unit}${context}`;
|
|
108
|
-
}
|
|
109
|
-
// If we can't find anything meaningful, show a generic message
|
|
110
|
-
return 'Details available';
|
|
111
|
-
};
|
|
112
20
|
if (!data?.data || data.data.length === 0) {
|
|
113
21
|
return (_jsx("p", { style: { color: '#6b7280' }, children: "No SEO data available for this site." }));
|
|
114
22
|
}
|
|
@@ -119,7 +27,7 @@ export function SiteHealthSEO({ siteName }) {
|
|
|
119
27
|
return (_jsxs(_Fragment, { children: [_jsx("h4", { className: "health-site-name", children: siteData.site.replace('-', ' ') }), _jsxs("p", { className: "health-site-url", children: ["URL: ", siteData.url] }), siteData.scores.seo !== null && (_jsx("div", { className: "health-score-container", children: _jsxs("div", { className: "health-score-item", children: [_jsx("div", { className: "health-score-label", children: "SEO Score" }), _jsxs("div", { className: "health-score-value", style: { color: getScoreColor(siteData.scores.seo) }, children: [Math.round((siteData.scores.seo || 0) * 100), "%"] }), _jsx("div", { className: "health-score-bar", children: _jsx("div", { className: "health-score-fill", style: {
|
|
120
28
|
width: `${(siteData.scores.seo || 0) * 100}%`,
|
|
121
29
|
backgroundColor: getScoreColor(siteData.scores.seo)
|
|
122
|
-
} }) })] }) })), siteData.categories.seo && siteData.categories.seo.audits.length > 0 && (_jsxs("div", { children: [_jsx("h5", { style: { fontSize: '1rem', fontWeight: '600', marginBottom: '1rem' }, children: "SEO Issues & Recommendations" }), _jsx("div", { className: "
|
|
30
|
+
} }) })] }) })), siteData.categories.seo && siteData.categories.seo.audits.length > 0 && (_jsxs("div", { children: [_jsx("h5", { style: { fontSize: '1rem', fontWeight: '600', marginBottom: '1rem' }, children: "SEO Issues & Recommendations" }), _jsx("div", { className: "health-audit-list", children: siteData.categories.seo.audits
|
|
123
31
|
.filter((audit) => audit.scoreDisplayMode !== 'notApplicable')
|
|
124
32
|
.sort((a, b) => (b.score || 0) - (a.score || 0))
|
|
125
33
|
.slice(0, 20)
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// Shared utilities for site-health components
|
|
2
|
+
// This file contains common functions used across multiple site-health components
|
|
3
|
+
import { getScoreIndicator } from './site-health-indicators';
|
|
4
|
+
/**
|
|
5
|
+
* Gets the icon for a score
|
|
6
|
+
*/
|
|
7
|
+
export function getAuditScoreIcon(score) {
|
|
8
|
+
return getScoreIndicator(score).icon;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Gets the color for a score
|
|
12
|
+
*/
|
|
13
|
+
export function getScoreColor(score) {
|
|
14
|
+
return getScoreIndicator(score).color;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Formats a score for display
|
|
18
|
+
*/
|
|
19
|
+
export function formatScore(score) {
|
|
20
|
+
if (score === null)
|
|
21
|
+
return 'N/A';
|
|
22
|
+
return `${Math.round(score * 100)}%`;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Formats audit item details for display
|
|
26
|
+
*/
|
|
27
|
+
export function formatAuditItem(item, auditTitle) {
|
|
28
|
+
// Handle URLs
|
|
29
|
+
if (item.url && typeof item.url === 'string') {
|
|
30
|
+
return item.url;
|
|
31
|
+
}
|
|
32
|
+
// Handle sources (like JavaScript files)
|
|
33
|
+
if (item.source && typeof item.source === 'string') {
|
|
34
|
+
return item.source;
|
|
35
|
+
}
|
|
36
|
+
// Handle text descriptions
|
|
37
|
+
if (item.text && typeof item.text === 'string') {
|
|
38
|
+
return item.text;
|
|
39
|
+
}
|
|
40
|
+
// Handle entities (like "Google Tag Manager")
|
|
41
|
+
if (item.entity && typeof item.entity === 'string') {
|
|
42
|
+
return item.entity;
|
|
43
|
+
}
|
|
44
|
+
// Handle nodes with selectors
|
|
45
|
+
if (item.node && typeof item.node === 'object' && 'selector' in item.node) {
|
|
46
|
+
return `Element: ${item.node.selector}`;
|
|
47
|
+
}
|
|
48
|
+
// Handle nodes with snippets
|
|
49
|
+
if (item.node && typeof item.node === 'object' && 'snippet' in item.node) {
|
|
50
|
+
const snippet = item.node.snippet;
|
|
51
|
+
return `Element: ${snippet.length > 50 ? snippet.substring(0, 50) + '...' : snippet}`;
|
|
52
|
+
}
|
|
53
|
+
// Handle origins (like domains)
|
|
54
|
+
if (item.origin && typeof item.origin === 'string') {
|
|
55
|
+
return item.origin;
|
|
56
|
+
}
|
|
57
|
+
// Handle labels
|
|
58
|
+
if (item.label && typeof item.label === 'string') {
|
|
59
|
+
return item.label;
|
|
60
|
+
}
|
|
61
|
+
// Handle numeric values with units
|
|
62
|
+
if (item.value && typeof item.value === 'object' && 'type' in item.value && item.value.type === 'numeric') {
|
|
63
|
+
const value = item.value;
|
|
64
|
+
return `${value.value}${item.unit || ''}`;
|
|
65
|
+
}
|
|
66
|
+
// Handle statistics
|
|
67
|
+
if (item.statistic && typeof item.statistic === 'string' && item.value) {
|
|
68
|
+
if (typeof item.value === 'object' && 'type' in item.value && item.value.type === 'numeric') {
|
|
69
|
+
const value = item.value;
|
|
70
|
+
return `${item.statistic}: ${value.value}`;
|
|
71
|
+
}
|
|
72
|
+
return item.statistic;
|
|
73
|
+
}
|
|
74
|
+
// Handle timing data with audit context
|
|
75
|
+
if (typeof item === 'number') {
|
|
76
|
+
let context = '';
|
|
77
|
+
if (auditTitle) {
|
|
78
|
+
if (auditTitle.toLowerCase().includes('server') || auditTitle.toLowerCase().includes('backend')) {
|
|
79
|
+
context = ' server response';
|
|
80
|
+
}
|
|
81
|
+
else if (auditTitle.toLowerCase().includes('network') || auditTitle.toLowerCase().includes('request')) {
|
|
82
|
+
context = ' network request';
|
|
83
|
+
}
|
|
84
|
+
else if (auditTitle.toLowerCase().includes('render') || auditTitle.toLowerCase().includes('blocking')) {
|
|
85
|
+
context = ' render blocking';
|
|
86
|
+
}
|
|
87
|
+
else if (auditTitle.toLowerCase().includes('javascript') || auditTitle.toLowerCase().includes('js')) {
|
|
88
|
+
context = ' JavaScript';
|
|
89
|
+
}
|
|
90
|
+
else if (auditTitle.toLowerCase().includes('image') || auditTitle.toLowerCase().includes('media')) {
|
|
91
|
+
context = ' media resource';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return `${item.toFixed(2)}ms${context}`;
|
|
95
|
+
}
|
|
96
|
+
if (item.value && typeof item.value === 'number') {
|
|
97
|
+
const unit = item.unit || 'ms';
|
|
98
|
+
let context = '';
|
|
99
|
+
if (auditTitle && unit === 'ms') {
|
|
100
|
+
if (auditTitle.toLowerCase().includes('server')) {
|
|
101
|
+
context = ' server time';
|
|
102
|
+
}
|
|
103
|
+
else if (auditTitle.toLowerCase().includes('network')) {
|
|
104
|
+
context = ' network time';
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return `${item.value.toFixed(2)}${unit}${context}`;
|
|
108
|
+
}
|
|
109
|
+
// Handle timing data with more context
|
|
110
|
+
if (item.duration && typeof item.duration === 'number') {
|
|
111
|
+
const duration = item.duration;
|
|
112
|
+
let context = '';
|
|
113
|
+
if (item.url && typeof item.url === 'string') {
|
|
114
|
+
context = ` for ${item.url}`;
|
|
115
|
+
}
|
|
116
|
+
else if (item.source && typeof item.source === 'string') {
|
|
117
|
+
context = ` for ${item.source}`;
|
|
118
|
+
}
|
|
119
|
+
else if (item.name && typeof item.name === 'string') {
|
|
120
|
+
context = ` for ${item.name}`;
|
|
121
|
+
}
|
|
122
|
+
else if (item.path && typeof item.path === 'string') {
|
|
123
|
+
context = ` for ${item.path}`;
|
|
124
|
+
}
|
|
125
|
+
else if (item.request && typeof item.request === 'string') {
|
|
126
|
+
context = ` for ${item.request}`;
|
|
127
|
+
}
|
|
128
|
+
return `${duration.toFixed(2)}ms${context}`;
|
|
129
|
+
}
|
|
130
|
+
// Handle response times
|
|
131
|
+
if (item.responseTime && typeof item.responseTime === 'number') {
|
|
132
|
+
const url = (item.url && typeof item.url === 'string') ? ` (${item.url})` : '';
|
|
133
|
+
return `${item.responseTime.toFixed(2)}ms response time${url}`;
|
|
134
|
+
}
|
|
135
|
+
// Handle start/end times
|
|
136
|
+
if ((item.startTime || item.endTime) && typeof (item.startTime || item.endTime) === 'number') {
|
|
137
|
+
const start = item.startTime && typeof item.startTime === 'number' ? item.startTime.toFixed(2) : '?';
|
|
138
|
+
const end = item.endTime && typeof item.endTime === 'number' ? item.endTime.toFixed(2) : '?';
|
|
139
|
+
const url = (item.url && typeof item.url === 'string') ? ` for ${item.url}` : '';
|
|
140
|
+
return `${start}ms - ${end}ms${url}`;
|
|
141
|
+
}
|
|
142
|
+
// Handle transfer size with timing
|
|
143
|
+
if (item.transferSize && typeof item.transferSize === 'number' && item.duration && typeof item.duration === 'number') {
|
|
144
|
+
const size = (item.transferSize / 1024).toFixed(1);
|
|
145
|
+
const time = item.duration.toFixed(2);
|
|
146
|
+
const url = (item.url && typeof item.url === 'string') ? ` (${item.url})` : '';
|
|
147
|
+
return `${size} KB in ${time}ms${url}`;
|
|
148
|
+
}
|
|
149
|
+
// Handle main thread time
|
|
150
|
+
if (item.mainThreadTime && typeof item.mainThreadTime === 'number') {
|
|
151
|
+
return `${item.mainThreadTime.toFixed(1)}ms`;
|
|
152
|
+
}
|
|
153
|
+
// For other objects, try to find a meaningful display
|
|
154
|
+
if (item.group && typeof item.group === 'string') {
|
|
155
|
+
return item.group;
|
|
156
|
+
}
|
|
157
|
+
if (item.type && typeof item.type === 'string') {
|
|
158
|
+
return item.type;
|
|
159
|
+
}
|
|
160
|
+
// If we can't find anything meaningful, provide a generic description
|
|
161
|
+
// This handles raw timing data that might be from various performance metrics
|
|
162
|
+
if (typeof item === 'number') {
|
|
163
|
+
return `${item.toFixed(2)}ms`;
|
|
164
|
+
}
|
|
165
|
+
if (item.value && typeof item.value === 'number') {
|
|
166
|
+
const unit = item.unit || 'ms';
|
|
167
|
+
return `${item.value.toFixed(2)}${unit}`;
|
|
168
|
+
}
|
|
169
|
+
return 'Performance metric data available';
|
|
170
|
+
}
|
|
@@ -67,8 +67,7 @@
|
|
|
67
67
|
|
|
68
68
|
.site-health-select select:focus {
|
|
69
69
|
outline: none;
|
|
70
|
-
|
|
71
|
-
ring-color: var(--color-primary);
|
|
70
|
+
box-shadow: 0 0 0 2px var(--color-primary);
|
|
72
71
|
border-color: var(--color-primary);
|
|
73
72
|
}
|
|
74
73
|
|
|
@@ -418,10 +417,41 @@ td {
|
|
|
418
417
|
|
|
419
418
|
table {
|
|
420
419
|
width: 100%;
|
|
421
|
-
table-layout: fixed;
|
|
422
420
|
}
|
|
423
421
|
|
|
424
422
|
th, td {
|
|
425
423
|
word-wrap: break-word;
|
|
426
424
|
overflow-wrap: break-word;
|
|
427
425
|
}
|
|
426
|
+
|
|
427
|
+
/* Helper & Semantic classes */
|
|
428
|
+
.health-audit-list > * + * { margin-top: 0.0rem; }
|
|
429
|
+
.health-section-list > * + * { margin-top: 1rem; }
|
|
430
|
+
|
|
431
|
+
.health-text-muted { color: #9ca3af; }
|
|
432
|
+
.health-text-secondary { color: var(--color-text-secondary); }
|
|
433
|
+
.health-text-error { color: var(--color-error); }
|
|
434
|
+
|
|
435
|
+
.health-visualization-placeholder {
|
|
436
|
+
display: flex;
|
|
437
|
+
align-items: center;
|
|
438
|
+
justify-content: center;
|
|
439
|
+
height: 16rem;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.health-version-tag {
|
|
443
|
+
display: inline-block;
|
|
444
|
+
padding: 0.25rem 0.5rem;
|
|
445
|
+
font-size: 0.75rem;
|
|
446
|
+
line-height: 1;
|
|
447
|
+
background-color: #dcfce7;
|
|
448
|
+
color: #166534;
|
|
449
|
+
border-radius: 0.25rem;
|
|
450
|
+
font-weight: 500;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.health-empty-state {
|
|
454
|
+
text-align: center;
|
|
455
|
+
padding: 1rem 0;
|
|
456
|
+
color: var(--color-text-secondary);
|
|
457
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import React from 'react';
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
4
|
import PropTypes from 'prop-types';
|
|
5
5
|
import Image from 'next/image';
|
|
6
6
|
import { buildCloudinaryUrl } from './cloudinary';
|
|
@@ -67,8 +67,27 @@ SmartImage.propTypes = {
|
|
|
67
67
|
export function SmartImage(props) {
|
|
68
68
|
const config = usePixelatedConfig();
|
|
69
69
|
const cloudCfg = config?.cloudinary;
|
|
70
|
-
|
|
70
|
+
// State to track current variant - only changes on actual errors (rare)
|
|
71
|
+
const [currentVariant, setCurrentVariant] = useState(props.variant || 'cloudinary');
|
|
72
|
+
const handleError = (error) => {
|
|
73
|
+
if (currentVariant === 'cloudinary') {
|
|
74
|
+
console.warn(`SmartImage: Cloudinary variant failed for "${props.src}", falling back to Next.js Image`, error);
|
|
75
|
+
setCurrentVariant('nextjs');
|
|
76
|
+
}
|
|
77
|
+
else if (currentVariant === 'nextjs') {
|
|
78
|
+
console.warn(`SmartImage: Next.js Image variant failed for "${props.src}", falling back to HTML img`, error);
|
|
79
|
+
setCurrentVariant('img');
|
|
80
|
+
}
|
|
81
|
+
// No more fallbacks after 'img'
|
|
82
|
+
};
|
|
83
|
+
// Reset variant if props change (different image)
|
|
84
|
+
React.useEffect(() => {
|
|
85
|
+
setCurrentVariant(props.variant || 'cloudinary');
|
|
86
|
+
}, [props.src, props.variant]);
|
|
87
|
+
const variant = currentVariant;
|
|
71
88
|
const newProps = { ...props };
|
|
89
|
+
// Always create ref to maintain consistent hook count across re-renders
|
|
90
|
+
const imgRef = React.useRef(null);
|
|
72
91
|
newProps.cloudinaryEnv = safeString(props.cloudinaryEnv ?? cloudCfg?.product_env);
|
|
73
92
|
newProps.cloudinaryDomain = safeString(cloudCfg?.baseUrl ?? CLOUDINARY_DOMAIN);
|
|
74
93
|
newProps.cloudinaryTransforms = safeString(CLOUDINARY_TRANSFORMS ?? cloudCfg?.transforms);
|
|
@@ -102,7 +121,7 @@ export function SmartImage(props) {
|
|
|
102
121
|
});
|
|
103
122
|
if (newProps.width) {
|
|
104
123
|
const widths = [Math.ceil(newProps.width * 0.5), newProps.width, Math.ceil(newProps.width * 1.5), Math.ceil(newProps.width * 2)];
|
|
105
|
-
newProps.srcSet = generateSrcSet(
|
|
124
|
+
newProps.srcSet = generateSrcSet(newProps.src, newProps.cloudinaryEnv, widths, {
|
|
106
125
|
quality: newProps.quality,
|
|
107
126
|
transforms: newProps.cloudinaryTransforms ?? undefined,
|
|
108
127
|
cloudinaryDomain: newProps.cloudinaryDomain
|
|
@@ -111,7 +130,7 @@ export function SmartImage(props) {
|
|
|
111
130
|
}
|
|
112
131
|
else {
|
|
113
132
|
const breakpoints = [320, 640, 768, 1024, 1280, 1536];
|
|
114
|
-
newProps.srcSet = generateSrcSet(
|
|
133
|
+
newProps.srcSet = generateSrcSet(newProps.src, newProps.cloudinaryEnv, breakpoints, {
|
|
115
134
|
quality: newProps.quality,
|
|
116
135
|
transforms: newProps.cloudinaryTransforms ?? undefined,
|
|
117
136
|
cloudinaryDomain: newProps.cloudinaryDomain
|
|
@@ -135,13 +154,14 @@ export function SmartImage(props) {
|
|
|
135
154
|
delete newProps.cloudinaryTransforms;
|
|
136
155
|
if (variant !== 'img') {
|
|
137
156
|
try {
|
|
138
|
-
return (_jsx(Image, { ...newProps, src: newProps.src, alt: newProps.alt }));
|
|
157
|
+
return (_jsx(Image, { ...newProps, src: newProps.src, alt: newProps.alt, onError: handleError }));
|
|
139
158
|
}
|
|
140
159
|
catch (e) {
|
|
141
|
-
|
|
142
|
-
|
|
160
|
+
console.warn(`SmartImage: Next.js Image threw exception for "${props.src}", falling back to plain img`, e);
|
|
161
|
+
// Force fallback to img variant
|
|
162
|
+
setCurrentVariant('img');
|
|
143
163
|
}
|
|
144
164
|
}
|
|
145
165
|
/* ===== IMG VARIANT ===== */
|
|
146
|
-
return (_jsx("img", { ...newProps, ref:
|
|
166
|
+
return (_jsx("img", { ...newProps, ref: imgRef, alt: newProps.alt }));
|
|
147
167
|
}
|
|
@@ -25,8 +25,8 @@ export function Table(props) {
|
|
|
25
25
|
function getHeadings(data) {
|
|
26
26
|
const headings = Object.keys(data[0]).map((key, i) => {
|
|
27
27
|
return (props.sortable && props.sortable == true)
|
|
28
|
-
? _jsxs("th", { onClick: () => { sortTable(key); }, children: [key, " ", _jsx("span", { className: "sortArrow" })] }, i)
|
|
29
|
-
: _jsx("th", { children: key }, i);
|
|
28
|
+
? _jsxs("th", { onClick: () => { sortTable(key); }, children: [_jsx("span", { children: key }), " ", _jsx("span", { className: "sortArrow" })] }, i)
|
|
29
|
+
: _jsx("th", { children: _jsx("span", { children: key }) }, i);
|
|
30
30
|
});
|
|
31
31
|
return _jsx("tr", { children: headings });
|
|
32
32
|
}
|
|
@@ -229,13 +229,22 @@ export function ConfigBuilder(props) {
|
|
|
229
229
|
...field.props,
|
|
230
230
|
value: (config.visualdesign && config.visualdesign[field.props.name]) ? (config.visualdesign[field.props.name].value ?? config.visualdesign[field.props.name]) : '',
|
|
231
231
|
defaultValue: (config.visualdesign && config.visualdesign[field.props.name]) ? (config.visualdesign[field.props.name].value ?? config.visualdesign[field.props.name]) : field.props.defaultValue || '',
|
|
232
|
-
onChange: (
|
|
233
|
-
|
|
232
|
+
onChange: (value) => {
|
|
233
|
+
// Handle both direct values and event objects
|
|
234
|
+
let actualValue = value;
|
|
235
|
+
if (value && typeof value === 'object' && value.target) {
|
|
236
|
+
actualValue = value.target.value;
|
|
237
|
+
}
|
|
234
238
|
setConfig((prev) => ({
|
|
235
239
|
...prev,
|
|
236
240
|
visualdesign: {
|
|
237
241
|
...(prev.visualdesign || {}),
|
|
238
|
-
[field.props.name]: {
|
|
242
|
+
[field.props.name]: {
|
|
243
|
+
...(prev.visualdesign && prev.visualdesign[field.props.name]
|
|
244
|
+
? prev.visualdesign[field.props.name]
|
|
245
|
+
: {}),
|
|
246
|
+
value: actualValue
|
|
247
|
+
}
|
|
239
248
|
}
|
|
240
249
|
}));
|
|
241
250
|
}
|
|
@@ -23,6 +23,7 @@ export * from './components/admin/site-health/site-health-indicators';
|
|
|
23
23
|
export * from './components/admin/site-health/site-health-on-site-seo.integration';
|
|
24
24
|
export * from './components/admin/site-health/site-health-types';
|
|
25
25
|
export * from './components/admin/site-health/site-health-performance';
|
|
26
|
+
export * from './components/admin/site-health/site-health-utils';
|
|
26
27
|
export * from './components/admin/site-health/site-health-cache';
|
|
27
28
|
export * from './components/admin/site-health/site-health-indicators';
|
|
28
29
|
export * from './components/admin/site-health/site-health-on-site-seo.integration';
|
|
@@ -14,6 +14,7 @@ export * from './components/admin/site-health/site-health-cloudwatch.integration
|
|
|
14
14
|
export * from './components/admin/site-health/seo-constants';
|
|
15
15
|
export * from './components/admin/site-health/site-health-security.integration';
|
|
16
16
|
export * from './components/admin/site-health/site-health-types';
|
|
17
|
+
export * from './components/admin/site-health/site-health-utils';
|
|
17
18
|
export * from './components/admin/site-health/site-health-uptime.integration';
|
|
18
19
|
export * from './components/admin/sites/sites.integration';
|
|
19
20
|
export * from './components/cms/contentful.management';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"site-health-accessibility.d.ts","sourceRoot":"","sources":["../../../../../src/components/admin/site-health/site-health-accessibility.tsx"],"names":[],"mappings":"AAGA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"site-health-accessibility.d.ts","sourceRoot":"","sources":["../../../../../src/components/admin/site-health/site-health-accessibility.tsx"],"names":[],"mappings":"AAGA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AASnD,MAAM,MAAM,2BAA2B,GAAG,UAAU,CAAC,OAAO,uBAAuB,CAAC,SAAS,CAAC,CAAC;AAC/F,wBAAgB,uBAAuB,CAAC,EAAE,QAAQ,EAAE,EAAE,2BAA2B,2CAsJhF;yBAtJe,uBAAuB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"site-health-on-site-seo.d.ts","sourceRoot":"","sources":["../../../../../src/components/admin/site-health/site-health-on-site-seo.tsx"],"names":[],"mappings":"AAGA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"site-health-on-site-seo.d.ts","sourceRoot":"","sources":["../../../../../src/components/admin/site-health/site-health-on-site-seo.tsx"],"names":[],"mappings":"AAGA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAsJnD,MAAM,MAAM,uBAAuB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,SAAS,CAAC,CAAC;AACvF,wBAAgB,mBAAmB,CAAC,EAAE,QAAQ,EAAE,EAAE,uBAAuB,2CAgIxE;yBAhIe,mBAAmB"}
|