PageSpeed monitoring, Polar billing, sidebar polish, frontend consistency audit #68
@@ -228,12 +228,23 @@ export default function PageSpeedPage() {
|
|||||||
score: c.performance_score,
|
score: c.performance_score,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// * Parse audits into groups
|
// * Parse audits into groups by Lighthouse category
|
||||||
const audits = currentCheck?.audits ?? []
|
const audits = currentCheck?.audits ?? []
|
||||||
const failingAudits = audits
|
const passed = audits.filter(a => a.category === 'passed')
|
||||||
.filter(a => a.category !== 'passed')
|
|
||||||
|
const categoryGroups = [
|
||||||
|
{ key: 'performance', label: 'Performance' },
|
||||||
|
{ key: 'accessibility', label: 'Accessibility' },
|
||||||
|
{ key: 'best-practices', label: 'Best Practices' },
|
||||||
|
{ key: 'seo', label: 'SEO' },
|
||||||
|
]
|
||||||
|
|
||||||
|
// * Build per-category failing audits, sorted by impact
|
||||||
|
const auditsByGroup: Record<string, typeof audits> = {}
|
||||||
|
for (const group of categoryGroups) {
|
||||||
|
auditsByGroup[group.key] = audits
|
||||||
|
.filter(a => a.category !== 'passed' && a.group === group.key)
|
||||||
.sort((a, b) => {
|
.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 1
|
if (a.category !== 'opportunity' && b.category === 'opportunity') return 1
|
||||||
if (a.category === 'opportunity' && b.category === 'opportunity') {
|
if (a.category === 'opportunity' && b.category === 'opportunity') {
|
||||||
@@ -241,7 +252,7 @@ export default function PageSpeedPage() {
|
|||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
const passed = audits.filter(a => a.category === 'passed')
|
}
|
||||||
|
|
||||||
// * Core Web Vitals metrics
|
// * Core Web Vitals metrics
|
||||||
const metrics = [
|
const metrics = [
|
||||||
@@ -503,32 +514,39 @@ export default function PageSpeedPage() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Section 4 — Diagnostics */}
|
{/* Section 4 — Diagnostics by Category */}
|
||||||
{audits.length > 0 && (
|
{audits.length > 0 && (
|
||||||
<div className="bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 rounded-2xl p-6 sm:p-8">
|
<div className="space-y-6">
|
||||||
|
{categoryGroups.map(group => {
|
||||||
|
const groupAudits = auditsByGroup[group.key] ?? []
|
||||||
|
const groupPassed = passed.filter(a => a.group === group.key)
|
||||||
|
if (groupAudits.length === 0 && groupPassed.length === 0) return null
|
||||||
|
return (
|
||||||
|
<div key={group.key} className="bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 rounded-2xl p-6 sm:p-8">
|
||||||
<h3 className="text-xs font-semibold text-neutral-500 dark:text-neutral-400 uppercase tracking-wider mb-4">
|
<h3 className="text-xs font-semibold text-neutral-500 dark:text-neutral-400 uppercase tracking-wider mb-4">
|
||||||
Diagnostics
|
{group.label}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
{/* Failing audits — flat list sorted by impact */}
|
{groupAudits.length > 0 && (
|
||||||
{failingAudits.length > 0 && (
|
|
||||||
<div className="divide-y divide-neutral-100 dark:divide-neutral-800">
|
<div className="divide-y divide-neutral-100 dark:divide-neutral-800">
|
||||||
{failingAudits.map(audit => <AuditRow key={audit.id} audit={audit} />)}
|
{groupAudits.map(audit => <AuditRow key={audit.id} audit={audit} />)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Passed audits — collapsed */}
|
{groupPassed.length > 0 && (
|
||||||
{passed.length > 0 && (
|
|
||||||
<details className="mt-4">
|
<details className="mt-4">
|
||||||
<summary className="cursor-pointer text-sm font-medium text-neutral-500 dark:text-neutral-400 select-none hover:text-neutral-700 dark:hover:text-neutral-300 transition-colors">
|
<summary className="cursor-pointer text-sm font-medium text-neutral-500 dark:text-neutral-400 select-none hover:text-neutral-700 dark:hover:text-neutral-300 transition-colors">
|
||||||
<span className="ml-1">{passed.length} passed audit{passed.length !== 1 ? 's' : ''}</span>
|
<span className="ml-1">{groupPassed.length} passed audit{groupPassed.length !== 1 ? 's' : ''}</span>
|
||||||
</summary>
|
</summary>
|
||||||
<div className="mt-2 divide-y divide-neutral-100 dark:divide-neutral-800">
|
<div className="mt-2 divide-y divide-neutral-100 dark:divide-neutral-800">
|
||||||
{passed.map(audit => <AuditRow key={audit.id} audit={audit} />)}
|
{groupPassed.map(audit => <AuditRow key={audit.id} audit={audit} />)}
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export interface AuditSummary {
|
|||||||
display_value?: string
|
display_value?: string
|
||||||
savings_ms?: number
|
savings_ms?: number
|
||||||
category: 'opportunity' | 'diagnostic' | 'passed'
|
category: 'opportunity' | 'diagnostic' | 'passed'
|
||||||
|
group?: string // "performance", "accessibility", "best-practices", "seo"
|
||||||
details?: AuditDetailItem[]
|
details?: AuditDetailItem[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user