@pixelated-tech/components 3.5.13 → 3.6.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.
- package/README.md +2 -0
- package/dist/components/admin/site-health/google.api.integration.js +258 -0
- package/dist/components/admin/site-health/google.api.utils.js +47 -0
- package/dist/components/admin/site-health/site-health-accessibility.js +5 -103
- package/dist/components/admin/site-health/site-health-axe-core.js +4 -10
- package/dist/components/admin/site-health/site-health-cloudwatch.js +24 -26
- package/dist/components/admin/site-health/site-health-dependency-vulnerabilities.js +4 -10
- package/dist/components/admin/site-health/site-health-github.js +9 -15
- package/dist/components/admin/site-health/site-health-google-analytics.integration.js +1 -107
- package/dist/components/admin/site-health/site-health-google-analytics.js +21 -29
- package/dist/components/admin/site-health/site-health-google-search-console.integration.js +1 -113
- package/dist/components/admin/site-health/site-health-google-search-console.js +22 -28
- package/dist/components/admin/site-health/site-health-on-site-seo.js +8 -82
- package/dist/components/admin/site-health/site-health-overview.js +5 -19
- package/dist/components/admin/site-health/site-health-performance.js +5 -167
- package/dist/components/admin/site-health/site-health-security.js +7 -148
- package/dist/components/admin/site-health/site-health-seo.js +5 -103
- package/dist/components/admin/site-health/site-health-template.js +68 -43
- package/dist/components/admin/site-health/site-health-uptime.js +4 -9
- package/dist/components/admin/site-health/site-health-utils.js +170 -0
- package/dist/components/admin/site-health/site-health.css +8 -9
- package/dist/index.adminclient.js +2 -5
- package/dist/index.adminserver.js +6 -4
- package/dist/index.js +39 -45
- package/dist/types/components/admin/site-health/google.api.integration.d.ts +82 -0
- package/dist/types/components/admin/site-health/google.api.integration.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/google.api.utils.d.ts +32 -0
- package/dist/types/components/admin/site-health/google.api.utils.d.ts.map +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-axe-core.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-cloudwatch.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-dependency-vulnerabilities.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-github.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-google-analytics.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-google-analytics.integration.d.ts +1 -21
- package/dist/types/components/admin/site-health/site-health-google-analytics.integration.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-google-search-console.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-google-search-console.integration.d.ts +1 -41
- package/dist/types/components/admin/site-health/site-health-google-search-console.integration.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-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-template.d.ts +8 -1
- package/dist/types/components/admin/site-health/site-health-template.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-uptime.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/index.adminclient.d.ts +2 -5
- package/dist/types/index.adminserver.d.ts +6 -4
- package/dist/types/index.d.ts +38 -44
- package/dist/types/stories/admin/site-health.stories.d.ts.map +1 -1
- package/dist/types/tests/google.api.integration.test.d.ts +2 -0
- package/dist/types/tests/google.api.integration.test.d.ts.map +1 -0
- package/dist/types/tests/google.api.utils.test.d.ts +5 -0
- package/dist/types/tests/google.api.utils.test.d.ts.map +1 -0
- package/package.json +1 -1
- package/dist/components/admin/site-health/google-api-auth.js +0 -69
- package/dist/types/components/admin/site-health/google-api-auth.d.ts +0 -37
- package/dist/types/components/admin/site-health/google-api-auth.d.ts.map +0 -1
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { useCallback } from 'react';
|
|
4
3
|
import PropTypes from 'prop-types';
|
|
5
4
|
import { SiteHealthTemplate } from './site-health-template';
|
|
6
|
-
import {
|
|
5
|
+
import { formatAuditItem, getAuditScoreIcon, getScoreColor, formatScore } from './site-health-utils';
|
|
7
6
|
SiteHealthPerformance.propTypes = {
|
|
8
7
|
siteName: PropTypes.string.isRequired,
|
|
9
8
|
};
|
|
10
9
|
export function SiteHealthPerformance({ siteName }) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
throw new Error(result.error || 'Failed to fetch Core Web Vitals data');
|
|
16
|
-
}
|
|
17
|
-
return result;
|
|
18
|
-
}, []);
|
|
19
|
-
return (_jsx(SiteHealthTemplate, { siteName: siteName, title: "PageSpeed - Performance", fetchData: fetchCWVData, children: (data) => {
|
|
10
|
+
return (_jsx(SiteHealthTemplate, { siteName: siteName, title: "PageSpeed - Performance", endpoint: {
|
|
11
|
+
endpoint: '/api/site-health/core-web-vitals',
|
|
12
|
+
responseTransformer: (result) => result, // Result is already in the correct format
|
|
13
|
+
}, children: (data) => {
|
|
20
14
|
if (!data?.data || data.data.length === 0) {
|
|
21
15
|
return (_jsx("p", { style: { color: '#6b7280' }, children: "No site health data available for this site." }));
|
|
22
16
|
}
|
|
@@ -24,162 +18,6 @@ export function SiteHealthPerformance({ siteName }) {
|
|
|
24
18
|
if (siteData.status === 'error') {
|
|
25
19
|
return (_jsxs("p", { style: { color: '#ef4444', fontSize: '0.875rem' }, children: ["Error: ", siteData.error] }));
|
|
26
20
|
}
|
|
27
|
-
const getAuditScoreIcon = (score) => {
|
|
28
|
-
return getScoreIndicator(score).icon;
|
|
29
|
-
};
|
|
30
|
-
const getScoreColor = (score) => {
|
|
31
|
-
return getScoreIndicator(score).color;
|
|
32
|
-
};
|
|
33
|
-
const formatScore = (score) => {
|
|
34
|
-
if (score === null)
|
|
35
|
-
return 'N/A';
|
|
36
|
-
return `${Math.round(score * 100)}%`;
|
|
37
|
-
};
|
|
38
|
-
// Helper function to display audit item details
|
|
39
|
-
const formatAuditItem = (item, auditTitle) => {
|
|
40
|
-
// Handle URLs
|
|
41
|
-
if (item.url && typeof item.url === 'string') {
|
|
42
|
-
return item.url;
|
|
43
|
-
}
|
|
44
|
-
// Handle sources (like JavaScript files)
|
|
45
|
-
if (item.source && typeof item.source === 'string') {
|
|
46
|
-
return item.source;
|
|
47
|
-
}
|
|
48
|
-
// Handle text descriptions
|
|
49
|
-
if (item.text && typeof item.text === 'string') {
|
|
50
|
-
return item.text;
|
|
51
|
-
}
|
|
52
|
-
// Handle entities (like "Google Tag Manager")
|
|
53
|
-
if (item.entity && typeof item.entity === 'string') {
|
|
54
|
-
return item.entity;
|
|
55
|
-
}
|
|
56
|
-
// Handle nodes with selectors
|
|
57
|
-
if (item.node && typeof item.node === 'object' && 'selector' in item.node) {
|
|
58
|
-
return `Element: ${item.node.selector}`;
|
|
59
|
-
}
|
|
60
|
-
// Handle nodes with snippets
|
|
61
|
-
if (item.node && typeof item.node === 'object' && 'snippet' in item.node) {
|
|
62
|
-
const snippet = item.node.snippet;
|
|
63
|
-
return `Element: ${snippet.length > 50 ? snippet.substring(0, 50) + '...' : snippet}`;
|
|
64
|
-
}
|
|
65
|
-
// Handle origins (like domains)
|
|
66
|
-
if (item.origin && typeof item.origin === 'string') {
|
|
67
|
-
return item.origin;
|
|
68
|
-
}
|
|
69
|
-
// Handle labels
|
|
70
|
-
if (item.label && typeof item.label === 'string') {
|
|
71
|
-
return item.label;
|
|
72
|
-
}
|
|
73
|
-
// Handle numeric values with units
|
|
74
|
-
if (item.value && typeof item.value === 'object' && 'type' in item.value && item.value.type === 'numeric') {
|
|
75
|
-
const value = item.value;
|
|
76
|
-
return `${value.value}${item.unit || ''}`;
|
|
77
|
-
}
|
|
78
|
-
// Handle statistics
|
|
79
|
-
if (item.statistic && typeof item.statistic === 'string' && item.value) {
|
|
80
|
-
if (typeof item.value === 'object' && 'type' in item.value && item.value.type === 'numeric') {
|
|
81
|
-
const value = item.value;
|
|
82
|
-
return `${item.statistic}: ${value.value}`;
|
|
83
|
-
}
|
|
84
|
-
return item.statistic;
|
|
85
|
-
}
|
|
86
|
-
// Handle timing data with audit context
|
|
87
|
-
if (typeof item === 'number') {
|
|
88
|
-
let context = '';
|
|
89
|
-
if (auditTitle) {
|
|
90
|
-
if (auditTitle.toLowerCase().includes('server') || auditTitle.toLowerCase().includes('backend')) {
|
|
91
|
-
context = ' server response';
|
|
92
|
-
}
|
|
93
|
-
else if (auditTitle.toLowerCase().includes('network') || auditTitle.toLowerCase().includes('request')) {
|
|
94
|
-
context = ' network request';
|
|
95
|
-
}
|
|
96
|
-
else if (auditTitle.toLowerCase().includes('render') || auditTitle.toLowerCase().includes('blocking')) {
|
|
97
|
-
context = ' render blocking';
|
|
98
|
-
}
|
|
99
|
-
else if (auditTitle.toLowerCase().includes('javascript') || auditTitle.toLowerCase().includes('js')) {
|
|
100
|
-
context = ' JavaScript';
|
|
101
|
-
}
|
|
102
|
-
else if (auditTitle.toLowerCase().includes('image') || auditTitle.toLowerCase().includes('media')) {
|
|
103
|
-
context = ' media resource';
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return `${item.toFixed(2)}ms${context}`;
|
|
107
|
-
}
|
|
108
|
-
if (item.value && typeof item.value === 'number') {
|
|
109
|
-
const unit = item.unit || 'ms';
|
|
110
|
-
let context = '';
|
|
111
|
-
if (auditTitle && unit === 'ms') {
|
|
112
|
-
if (auditTitle.toLowerCase().includes('server')) {
|
|
113
|
-
context = ' server time';
|
|
114
|
-
}
|
|
115
|
-
else if (auditTitle.toLowerCase().includes('network')) {
|
|
116
|
-
context = ' network time';
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return `${item.value.toFixed(2)}${unit}${context}`;
|
|
120
|
-
}
|
|
121
|
-
// Handle timing data with more context
|
|
122
|
-
if (item.duration && typeof item.duration === 'number') {
|
|
123
|
-
const duration = item.duration;
|
|
124
|
-
let context = '';
|
|
125
|
-
if (item.url && typeof item.url === 'string') {
|
|
126
|
-
context = ` for ${item.url}`;
|
|
127
|
-
}
|
|
128
|
-
else if (item.source && typeof item.source === 'string') {
|
|
129
|
-
context = ` for ${item.source}`;
|
|
130
|
-
}
|
|
131
|
-
else if (item.name && typeof item.name === 'string') {
|
|
132
|
-
context = ` for ${item.name}`;
|
|
133
|
-
}
|
|
134
|
-
else if (item.path && typeof item.path === 'string') {
|
|
135
|
-
context = ` for ${item.path}`;
|
|
136
|
-
}
|
|
137
|
-
else if (item.request && typeof item.request === 'string') {
|
|
138
|
-
context = ` for ${item.request}`;
|
|
139
|
-
}
|
|
140
|
-
return `${duration.toFixed(2)}ms${context}`;
|
|
141
|
-
}
|
|
142
|
-
// Handle response times
|
|
143
|
-
if (item.responseTime && typeof item.responseTime === 'number') {
|
|
144
|
-
const url = (item.url && typeof item.url === 'string') ? ` (${item.url})` : '';
|
|
145
|
-
return `${item.responseTime.toFixed(2)}ms response time${url}`;
|
|
146
|
-
}
|
|
147
|
-
// Handle start/end times
|
|
148
|
-
if ((item.startTime || item.endTime) && typeof (item.startTime || item.endTime) === 'number') {
|
|
149
|
-
const start = item.startTime && typeof item.startTime === 'number' ? item.startTime.toFixed(2) : '?';
|
|
150
|
-
const end = item.endTime && typeof item.endTime === 'number' ? item.endTime.toFixed(2) : '?';
|
|
151
|
-
const url = (item.url && typeof item.url === 'string') ? ` for ${item.url}` : '';
|
|
152
|
-
return `${start}ms - ${end}ms${url}`;
|
|
153
|
-
}
|
|
154
|
-
// Handle transfer size with timing
|
|
155
|
-
if (item.transferSize && typeof item.transferSize === 'number' && item.duration && typeof item.duration === 'number') {
|
|
156
|
-
const size = (item.transferSize / 1024).toFixed(1);
|
|
157
|
-
const time = item.duration.toFixed(2);
|
|
158
|
-
const url = (item.url && typeof item.url === 'string') ? ` (${item.url})` : '';
|
|
159
|
-
return `${size} KB in ${time}ms${url}`;
|
|
160
|
-
}
|
|
161
|
-
// Handle main thread time
|
|
162
|
-
if (item.mainThreadTime && typeof item.mainThreadTime === 'number') {
|
|
163
|
-
return `${item.mainThreadTime.toFixed(1)}ms`;
|
|
164
|
-
}
|
|
165
|
-
// For other objects, try to find a meaningful display
|
|
166
|
-
if (item.group && typeof item.group === 'string') {
|
|
167
|
-
return item.group;
|
|
168
|
-
}
|
|
169
|
-
if (item.type && typeof item.type === 'string') {
|
|
170
|
-
return item.type;
|
|
171
|
-
}
|
|
172
|
-
// If we can't find anything meaningful, provide a generic description
|
|
173
|
-
// This handles raw timing data that might be from various performance metrics
|
|
174
|
-
if (typeof item === 'number') {
|
|
175
|
-
return `${item.toFixed(2)}ms`;
|
|
176
|
-
}
|
|
177
|
-
if (item.value && typeof item.value === 'number') {
|
|
178
|
-
const unit = item.unit || 'ms';
|
|
179
|
-
return `${item.value.toFixed(2)}${unit}`;
|
|
180
|
-
}
|
|
181
|
-
return 'Performance metric data available';
|
|
182
|
-
};
|
|
183
21
|
return (_jsxs(_Fragment, { children: [_jsx("h4", { className: "health-site-name", children: siteData.site.replace('-', ' ') }), _jsxs("p", { className: "health-site-url", children: ["URL: ", siteData.url] }), _jsx("div", { style: { marginBottom: '1.5rem' }, children: _jsxs("div", { className: "health-score-item", style: { width: '100%' }, children: [_jsx("div", { className: "health-score-label", children: "Performance Score" }), _jsx("div", { className: "health-score-value", style: { color: getScoreColor(siteData.scores.performance) }, children: formatScore(siteData.scores.performance) }), _jsx("div", { className: "health-score-bar", children: _jsx("div", { className: "health-score-fill", style: {
|
|
184
22
|
width: siteData.scores.performance !== null ? `${siteData.scores.performance * 100}%` : '0%',
|
|
185
23
|
backgroundColor: siteData.scores.performance !== null ? getScoreColor(siteData.scores.performance) : '#6b7280'
|
|
@@ -1,159 +1,18 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { useCallback } from 'react';
|
|
4
3
|
import PropTypes from 'prop-types';
|
|
5
4
|
import { SiteHealthTemplate } from './site-health-template';
|
|
6
|
-
import {
|
|
5
|
+
import { formatAuditItem, getAuditScoreIcon, getScoreColor } from './site-health-utils';
|
|
7
6
|
SiteHealthSecurity.propTypes = {
|
|
8
7
|
siteName: PropTypes.string.isRequired,
|
|
9
8
|
};
|
|
10
9
|
export function SiteHealthSecurity({ siteName }) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
};
|
|
18
|
-
}, []);
|
|
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
|
-
};
|
|
10
|
+
return (_jsx(SiteHealthTemplate, { siteName: siteName, title: "PageSpeed - Security", endpoint: {
|
|
11
|
+
endpoint: '/api/site-health/core-web-vitals',
|
|
12
|
+
responseTransformer: (result) => ({
|
|
13
|
+
psiData: result.success ? result : undefined
|
|
14
|
+
}),
|
|
15
|
+
}, children: (data) => {
|
|
157
16
|
const psiData = data?.psiData?.data?.[0];
|
|
158
17
|
if (!psiData) {
|
|
159
18
|
return (_jsx("p", { style: { color: '#6b7280' }, children: "No security data available for this site." }));
|
|
@@ -1,114 +1,16 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { useCallback } from 'react';
|
|
4
3
|
import PropTypes from 'prop-types';
|
|
5
4
|
import { SiteHealthTemplate } from './site-health-template';
|
|
6
|
-
import {
|
|
5
|
+
import { formatAuditItem, getAuditScoreIcon, getScoreColor } from './site-health-utils';
|
|
7
6
|
SiteHealthSEO.propTypes = {
|
|
8
7
|
siteName: PropTypes.string.isRequired,
|
|
9
8
|
};
|
|
10
9
|
export function SiteHealthSEO({ siteName }) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
throw new Error(result.error || 'Failed to fetch SEO data');
|
|
16
|
-
}
|
|
17
|
-
return result;
|
|
18
|
-
}, []);
|
|
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
|
-
};
|
|
10
|
+
return (_jsx(SiteHealthTemplate, { siteName: siteName, title: "PageSpeed - SEO", endpoint: {
|
|
11
|
+
endpoint: '/api/site-health/core-web-vitals',
|
|
12
|
+
responseTransformer: (result) => result, // Result is already in the correct format
|
|
13
|
+
}, children: (data) => {
|
|
112
14
|
if (!data?.data || data.data.length === 0) {
|
|
113
15
|
return (_jsx("p", { style: { color: '#6b7280' }, children: "No SEO data available for this site." }));
|
|
114
16
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { useEffect, useState } from 'react';
|
|
3
|
+
import { useEffect, useState, useCallback } from 'react';
|
|
4
4
|
import PropTypes from 'prop-types';
|
|
5
5
|
import { PageGridItem } from '../../general/semantic';
|
|
6
6
|
import "./site-health.css";
|
|
@@ -8,7 +8,14 @@ SiteHealthTemplate.propTypes = {
|
|
|
8
8
|
siteName: PropTypes.string.isRequired,
|
|
9
9
|
title: PropTypes.string,
|
|
10
10
|
children: PropTypes.func.isRequired,
|
|
11
|
-
|
|
11
|
+
endpoint: PropTypes.shape({
|
|
12
|
+
endpoint: PropTypes.string.isRequired,
|
|
13
|
+
method: PropTypes.oneOf(['GET', 'POST', 'PUT', 'DELETE']),
|
|
14
|
+
headers: PropTypes.object,
|
|
15
|
+
params: PropTypes.object,
|
|
16
|
+
body: PropTypes.any,
|
|
17
|
+
responseTransformer: PropTypes.func,
|
|
18
|
+
}),
|
|
12
19
|
enableCacheControl: PropTypes.bool,
|
|
13
20
|
columnSpan: PropTypes.number,
|
|
14
21
|
};
|
|
@@ -17,49 +24,67 @@ export function SiteHealthTemplate(props) {
|
|
|
17
24
|
const [data, setData] = useState(null);
|
|
18
25
|
const [loading, setLoading] = useState(false);
|
|
19
26
|
const [error, setError] = useState(null);
|
|
27
|
+
// Default fetch function for endpoint-based requests
|
|
28
|
+
const fetchFromEndpoint = useCallback(async (useCache = true) => {
|
|
29
|
+
const { endpoint: endpointUrl, method = 'GET', headers = {}, params = {}, body, responseTransformer } = typedProps.endpoint;
|
|
30
|
+
// Build URL with siteName parameter
|
|
31
|
+
const url = new URL(endpointUrl, window.location.origin);
|
|
32
|
+
url.searchParams.set('siteName', encodeURIComponent(typedProps.siteName));
|
|
33
|
+
// Add additional params
|
|
34
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
35
|
+
url.searchParams.set(key, value);
|
|
36
|
+
});
|
|
37
|
+
// Add cache control if not using cache
|
|
38
|
+
if (!useCache) {
|
|
39
|
+
url.searchParams.set('cache', 'false');
|
|
40
|
+
}
|
|
41
|
+
const response = await fetch(url.toString(), {
|
|
42
|
+
method,
|
|
43
|
+
headers: {
|
|
44
|
+
'Content-Type': 'application/json',
|
|
45
|
+
...headers,
|
|
46
|
+
},
|
|
47
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
48
|
+
});
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
51
|
+
}
|
|
52
|
+
const result = await response.json();
|
|
53
|
+
if (!result.success) {
|
|
54
|
+
throw new Error(result.error || 'API request failed');
|
|
55
|
+
}
|
|
56
|
+
// Apply response transformer if provided
|
|
57
|
+
return responseTransformer ? responseTransformer(result) : result;
|
|
58
|
+
}, [typedProps.endpoint, typedProps.siteName]);
|
|
59
|
+
const loadData = useCallback(async () => {
|
|
60
|
+
if (!typedProps.siteName) {
|
|
61
|
+
setData(null);
|
|
62
|
+
setLoading(false);
|
|
63
|
+
setError(null);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
setLoading(true);
|
|
67
|
+
setError(null);
|
|
68
|
+
try {
|
|
69
|
+
// Check for cache control from URL query parameters
|
|
70
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
71
|
+
const cacheParam = urlParams.get('cache');
|
|
72
|
+
const useCache = typedProps.enableCacheControl ?? true ? (cacheParam !== 'false') : true;
|
|
73
|
+
const result = await fetchFromEndpoint(useCache);
|
|
74
|
+
setData(result);
|
|
75
|
+
setError(null);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
setError(err instanceof Error ? err.message : 'Failed to load data');
|
|
79
|
+
setData(null);
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
setLoading(false);
|
|
83
|
+
}
|
|
84
|
+
}, [typedProps.siteName, fetchFromEndpoint, typedProps.enableCacheControl]);
|
|
20
85
|
useEffect(() => {
|
|
21
|
-
let isMounted = true;
|
|
22
|
-
const loadData = async () => {
|
|
23
|
-
if (!typedProps.siteName) {
|
|
24
|
-
if (isMounted) {
|
|
25
|
-
setData(null);
|
|
26
|
-
setLoading(false);
|
|
27
|
-
setError(null);
|
|
28
|
-
}
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
if (isMounted) {
|
|
32
|
-
setLoading(true);
|
|
33
|
-
setError(null);
|
|
34
|
-
}
|
|
35
|
-
try {
|
|
36
|
-
// Check for cache control from URL query parameters
|
|
37
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
38
|
-
const cacheParam = urlParams.get('cache');
|
|
39
|
-
const useCache = typedProps.enableCacheControl ? (cacheParam !== 'false') : true;
|
|
40
|
-
const result = await typedProps.fetchData(typedProps.siteName, useCache);
|
|
41
|
-
if (isMounted) {
|
|
42
|
-
setData(result);
|
|
43
|
-
setError(null);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
catch (err) {
|
|
47
|
-
if (isMounted) {
|
|
48
|
-
setError(err instanceof Error ? err.message : 'Failed to load data');
|
|
49
|
-
setData(null);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
finally {
|
|
53
|
-
if (isMounted) {
|
|
54
|
-
setLoading(false);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
86
|
loadData();
|
|
59
|
-
|
|
60
|
-
isMounted = false;
|
|
61
|
-
};
|
|
62
|
-
}, [typedProps.siteName, typedProps.fetchData]);
|
|
87
|
+
}, [loadData]);
|
|
63
88
|
// If no site selected, show nothing
|
|
64
89
|
if (!typedProps.siteName) {
|
|
65
90
|
return null;
|
|
@@ -6,15 +6,10 @@ SiteHealthUptime.propTypes = {
|
|
|
6
6
|
siteName: PropTypes.string.isRequired,
|
|
7
7
|
};
|
|
8
8
|
export function SiteHealthUptime({ siteName }) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
throw new Error('Failed to load health status');
|
|
14
|
-
}
|
|
15
|
-
return result;
|
|
16
|
-
};
|
|
17
|
-
return (_jsx(SiteHealthTemplate, { siteName: siteName, title: "Health Status", fetchData: fetchUptimeData, children: (data) => {
|
|
9
|
+
return (_jsx(SiteHealthTemplate, { siteName: siteName, title: "Health Status", endpoint: {
|
|
10
|
+
endpoint: '/api/site-health/uptime',
|
|
11
|
+
responseTransformer: (result) => result, // Result is already in the correct format
|
|
12
|
+
}, children: (data) => {
|
|
18
13
|
if (!data) {
|
|
19
14
|
return (_jsx("p", { style: { color: '#6b7280' }, children: "No uptime data available for this site." }));
|
|
20
15
|
}
|