import { useQuery } from '@tanstack/react-query'; import { apiFetch } from '../lib/api'; import PageScanner from './PageScanner'; import { DashboardSkeleton } from './Skeleton'; import type { PageScanSummary } from '../lib/types'; export default function Dashboard() { const { data: pageScans = {}, isLoading } = useQuery({ queryKey: ['page-scans'], queryFn: () => apiFetch>('/page-scans'), }); if (isLoading) return ; // Aggregate issue counts across all scanned pages const allIssues = Object.values(pageScans).flatMap((s) => s.issues ?? []); const total = allIssues.length; const critical = allIssues.filter((i) => i.severity === 'critical').length; const warning = allIssues.filter((i) => i.severity === 'warning').length; const notice = allIssues.filter((i) => i.severity === 'notice').length; const hasData = Object.keys(pageScans).length > 0; return (
{/* Stats row */} {hasData && ( <>

Automated accessibility score based on detected issues.{' '} Some issues may require manual testing. ⓘ

💡 Tip: Fix critical issues first for the biggest accessibility impact.

)} {/* Issues by page chart */} {hasData && (

Issues by Page

Top pages ranked by number of accessibility issues

)} {/* Pages & Posts scanner (main dashboard view) */}
); } // ── Issues by page bar chart ─────────────────────────────────── function IssuesByPage({ pageScans }: { pageScans: Record }) { const pages = Object.values(pageScans) .filter((s) => s.failed > 0) .sort((a, b) => b.failed - a.failed) .slice(0, 7); if (pages.length === 0) return (

No issues detected across scanned pages.

); const maxFailed = pages[0].failed || 1; return (
{pages.map((scan) => { const crit = scan.issues?.filter((i) => i.severity === 'critical').length ?? 0; const warn = scan.issues?.filter((i) => i.severity === 'warning').length ?? 0; const note = scan.issues?.filter((i) => i.severity === 'notice').length ?? 0; const pathname = (() => { try { return new URL(scan.url).pathname || '/'; } catch { return scan.url; } })(); const barPct = (scan.failed / maxFailed) * 100; return (
{pathname}
{crit > 0 && } {warn > 0 && } {note > 0 && }
{scan.failed}
); })}
Critical Warning Notice
); } // ── Stat card ────────────────────────────────────────────────── function StatCard({ label, value, variant }: { label: string; value: number; variant: string }) { return (
{value} {label}
); }