feat(pagespeed): manual check section, consistent dot indicators
- Add "Additional items to manually check" collapsed section - Replace triangle/square severity icons with consistent filled circles - Empty circle (border only) for informative/unscored audits
This commit is contained in:
@@ -247,9 +247,10 @@ export default function PageSpeedPage() {
|
|||||||
|
|
||||||
// * Build per-category failing audits, sorted by impact
|
// * Build per-category failing audits, sorted by impact
|
||||||
const auditsByGroup: Record<string, typeof audits> = {}
|
const auditsByGroup: Record<string, typeof audits> = {}
|
||||||
|
const manualByGroup: Record<string, typeof audits> = {}
|
||||||
for (const group of categoryGroups) {
|
for (const group of categoryGroups) {
|
||||||
auditsByGroup[group.key] = audits
|
auditsByGroup[group.key] = audits
|
||||||
.filter(a => a.category !== 'passed' && a.group === group.key)
|
.filter(a => a.category !== 'passed' && a.category !== 'manual' && a.group === group.key)
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
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
|
||||||
@@ -258,6 +259,7 @@ export default function PageSpeedPage() {
|
|||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
|
manualByGroup[group.key] = audits.filter(a => a.category === 'manual' && a.group === group.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// * Core Web Vitals metrics
|
// * Core Web Vitals metrics
|
||||||
@@ -509,7 +511,8 @@ export default function PageSpeedPage() {
|
|||||||
{categoryGroups.map(group => {
|
{categoryGroups.map(group => {
|
||||||
const groupAudits = auditsByGroup[group.key] ?? []
|
const groupAudits = auditsByGroup[group.key] ?? []
|
||||||
const groupPassed = passed.filter(a => a.group === group.key)
|
const groupPassed = passed.filter(a => a.group === group.key)
|
||||||
if (groupAudits.length === 0 && groupPassed.length === 0) return null
|
const groupManual = manualByGroup[group.key] ?? []
|
||||||
|
if (groupAudits.length === 0 && groupPassed.length === 0 && groupManual.length === 0) return null
|
||||||
return (
|
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">
|
<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">
|
||||||
{/* Category header with gauge */}
|
{/* Category header with gauge */}
|
||||||
@@ -532,6 +535,17 @@ export default function PageSpeedPage() {
|
|||||||
<AuditsBySubGroup audits={groupAudits} />
|
<AuditsBySubGroup audits={groupAudits} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{groupManual.length > 0 && (
|
||||||
|
<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">
|
||||||
|
<span className="ml-1">Additional items to manually check ({groupManual.length})</span>
|
||||||
|
</summary>
|
||||||
|
<div className="mt-2 divide-y divide-neutral-100 dark:divide-neutral-800">
|
||||||
|
{groupManual.map(audit => <AuditRow key={audit.id} audit={audit} />)}
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
)}
|
||||||
|
|
||||||
{groupPassed.length > 0 && (
|
{groupPassed.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">
|
||||||
@@ -629,19 +643,15 @@ function AuditsBySubGroup({ audits }: { audits: AuditSummary[] }) {
|
|||||||
// * Severity indicator based on audit score (pagespeed.web.dev style)
|
// * Severity indicator based on audit score (pagespeed.web.dev style)
|
||||||
function AuditSeverityIcon({ score }: { score: number | null }) {
|
function AuditSeverityIcon({ score }: { score: number | null }) {
|
||||||
if (score === null) {
|
if (score === null) {
|
||||||
// Empty circle for informative/unscored audits
|
return <span className="inline-block w-2.5 h-2.5 rounded-full border-2 border-neutral-400 flex-shrink-0" aria-label="Informative" />
|
||||||
return <span className="text-neutral-400 text-sm leading-none flex-shrink-0" aria-label="Informative">○</span>
|
|
||||||
}
|
}
|
||||||
if (score < 0.5) {
|
if (score < 0.5) {
|
||||||
// Red triangle for poor
|
return <span className="inline-block w-2.5 h-2.5 rounded-full bg-red-500 flex-shrink-0" aria-label="Poor" />
|
||||||
return <span className="text-red-500 text-sm leading-none flex-shrink-0" aria-label="Poor">▲</span>
|
|
||||||
}
|
}
|
||||||
if (score < 0.9) {
|
if (score < 0.9) {
|
||||||
// Amber square for needs improvement
|
return <span className="inline-block w-2.5 h-2.5 rounded-full bg-amber-500 flex-shrink-0" aria-label="Needs Improvement" />
|
||||||
return <span className="text-amber-500 text-sm leading-none flex-shrink-0" aria-label="Needs Improvement">■</span>
|
|
||||||
}
|
}
|
||||||
// Green circle for good
|
return <span className="inline-block w-2.5 h-2.5 rounded-full bg-emerald-500 flex-shrink-0" aria-label="Good" />
|
||||||
return <span className="text-emerald-500 text-sm leading-none flex-shrink-0" aria-label="Good">●</span>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// * Expandable audit row with description and detail items
|
// * Expandable audit row with description and detail items
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export interface AuditSummary {
|
|||||||
score: number | null
|
score: number | null
|
||||||
display_value?: string
|
display_value?: string
|
||||||
savings_ms?: number
|
savings_ms?: number
|
||||||
category: 'opportunity' | 'diagnostic' | 'passed'
|
category: 'opportunity' | 'diagnostic' | 'passed' | 'manual'
|
||||||
group?: string // "performance", "accessibility", "best-practices", "seo"
|
group?: string // "performance", "accessibility", "best-practices", "seo"
|
||||||
sub_group?: string // "a11y-names-labels", "a11y-contrast", etc.
|
sub_group?: string // "a11y-names-labels", "a11y-contrast", etc.
|
||||||
sub_group_title?: string // "Names and Labels", "Contrast", etc.
|
sub_group_title?: string // "Names and Labels", "Contrast", etc.
|
||||||
|
|||||||
Reference in New Issue
Block a user