diff --git a/app/sites/[id]/pagespeed/page.tsx b/app/sites/[id]/pagespeed/page.tsx index 8e86056..df1f307 100644 --- a/app/sites/[id]/pagespeed/page.tsx +++ b/app/sites/[id]/pagespeed/page.tsx @@ -230,22 +230,51 @@ export default function PageSpeedPage() { // * Parse audits into groups const audits = currentCheck?.audits ?? [] - const opportunities = audits - .filter(a => a.category === 'opportunity') - .sort((a, b) => (b.savings_ms ?? 0) - (a.savings_ms ?? 0)) - const diagnostics = audits.filter(a => a.category === 'diagnostic') + const failingAudits = audits + .filter(a => a.category !== 'passed') + .sort((a, b) => { + // Opportunities first (sorted by savings_ms desc), then diagnostics + if (a.category === 'opportunity' && b.category !== 'opportunity') return -1 + if (a.category !== 'opportunity' && b.category === 'opportunity') return 1 + if (a.category === 'opportunity' && b.category === 'opportunity') { + return (b.savings_ms ?? 0) - (a.savings_ms ?? 0) + } + return 0 + }) const passed = audits.filter(a => a.category === 'passed') // * Core Web Vitals metrics const metrics = [ - { key: 'lcp', label: 'Largest Contentful Paint', value: currentCheck?.lcp_ms ?? null }, - { key: 'cls', label: 'Cumulative Layout Shift', value: currentCheck?.cls ?? null }, - { key: 'tbt', label: 'Total Blocking Time', value: currentCheck?.tbt_ms ?? null }, { key: 'fcp', label: 'First Contentful Paint', value: currentCheck?.fcp_ms ?? null }, + { key: 'lcp', label: 'Largest Contentful Paint', value: currentCheck?.lcp_ms ?? null }, + { key: 'tbt', label: 'Total Blocking Time', value: currentCheck?.tbt_ms ?? null }, + { key: 'cls', label: 'Cumulative Layout Shift', value: currentCheck?.cls ?? null }, { key: 'si', label: 'Speed Index', value: currentCheck?.si_ms ?? null }, { key: 'tti', label: 'Time to Interactive', value: currentCheck?.tti_ms ?? null }, ] + // * Compact score helper for the hero section + const compactScores = [ + { label: 'Accessibility', score: currentCheck?.accessibility_score ?? null }, + { label: 'Best Practices', score: currentCheck?.best_practices_score ?? null }, + { label: 'SEO', score: currentCheck?.seo_score ?? null }, + ] + + function getScoreColor(score: number | null): string { + if (score === null) return '#6b7280' + if (score >= 90) return '#0cce6b' + if (score >= 50) return '#ffa400' + return '#ff4e42' + } + + function getMetricDotColor(metric: string, value: number | null): string { + if (value === null) return 'bg-neutral-400' + const status = getMetricStatus(metric, value) + if (status.label === 'Good') return 'bg-emerald-500' + if (status.label === 'Needs Improvement') return 'bg-amber-500' + return 'bg-red-500' + } + // * Enabled state — show full PageSpeed dashboard return (
{audit.description}
+{audit.description}
)} {/* Items table */} {audit.details && Array.isArray(audit.details) && audit.details.length > 0 && ( diff --git a/components/pagespeed/ScoreGauge.tsx b/components/pagespeed/ScoreGauge.tsx index 78886ca..7b9ceb4 100644 --- a/components/pagespeed/ScoreGauge.tsx +++ b/components/pagespeed/ScoreGauge.tsx @@ -3,6 +3,7 @@ interface ScoreGaugeProps { score: number | null label: string + size?: number } const RADIUS = 44 @@ -14,15 +15,17 @@ function getColor(score: number): string { return '#ff4e42' } -export default function ScoreGauge({ score, label }: ScoreGaugeProps) { +export default function ScoreGauge({ score, label, size = 120 }: ScoreGaugeProps) { const hasScore = score !== null && score !== undefined const displayScore = hasScore ? Math.round(score) : null const offset = hasScore ? CIRCUMFERENCE * (1 - score / 100) : CIRCUMFERENCE const color = hasScore ? getColor(score) : '#6b7280' + const fontSize = size >= 160 ? 'text-4xl' : 'text-2xl' + return (