import { useQuery } from '@tanstack/react-query' import { useCallback, useMemo, useState } from 'react' import { useSearchParams } from 'react-router-dom' const DEFAULT_WEBSITE_URL = 'https://www.communityone.com/' type LighthouseScores = { performance: number | null accessibility: number | null best_practices: number | null seo: number | null } export type LighthouseReportPayload = { scan_key: string batch_id: string jurisdiction_id: string website_url: string final_url: string | null scanned_at: string | null status: string lighthouse_version: string | null requested_url: string | null scores: LighthouseScores run_warnings: string[] screenshot_data_url: string | null } function scoreStroke(score: number | null): string { if (score == null) return '#94a3b8' if (score >= 90) return '#15803d' if (score >= 50) return '#ca8a04' return '#b91c1c' } function LighthouseGauge({ label, score, size = 'sm', }: { label: string score: number | null size?: 'sm' | 'lg' }) { const r = size === 'lg' ? 54 : 38 const c = 2 * Math.PI * r const pct = score == null ? 0 : Math.min(100, Math.max(0, score)) / 100 const offset = c * (1 - pct) const stroke = scoreStroke(score) const dim = size === 'lg' ? 136 : 100 const view = dim const cx = view / 2 const cy = view / 2 return (
{score == null ? '—' : score}
{label}
) } function WarningBox({ items }: { items: string[] }) { if (!items.length) return null return (

There were issues affecting this run of Lighthouse:

) } function formatErrorDetail(detail: unknown): string { if (typeof detail === 'string') return detail if (Array.isArray(detail)) { return detail .map((x) => (typeof x === 'object' && x && 'msg' in x ? String((x as { msg: string }).msg) : String(x))) .join('; ') } if (detail && typeof detail === 'object' && 'detail' in detail) { return formatErrorDetail((detail as { detail: unknown }).detail) } return 'Request failed' } async function fetchReport(websiteUrl: string, batchId?: string): Promise { const u = new URL('/api/lighthouse/report', window.location.origin) u.searchParams.set('website_url', websiteUrl.trim()) if (batchId?.trim()) u.searchParams.set('batch_id', batchId.trim()) const res = await fetch(u.toString(), { credentials: 'include' }) if (!res.ok) { let body: unknown = null try { body = await res.json() } catch { /* ignore */ } const msg = body && typeof body === 'object' && 'detail' in body ? formatErrorDetail((body as { detail: unknown }).detail) : `HTTP ${res.status}` throw new Error(msg) } return (await res.json()) as LighthouseReportPayload } export default function LighthouseReportPage() { const [searchParams, setSearchParams] = useSearchParams() const initialUrl = useMemo(() => { const q = searchParams.get('url')?.trim() return q && q.length >= 4 ? q : DEFAULT_WEBSITE_URL }, [searchParams]) const [urlInput, setUrlInput] = useState(initialUrl) const [batchInput, setBatchInput] = useState(() => searchParams.get('batch_id') ?? '') const [submittedUrl, setSubmittedUrl] = useState(initialUrl) const [submittedBatch, setSubmittedBatch] = useState(() => searchParams.get('batch_id') ?? '') const syncQuery = useCallback( (nextUrl: string, nextBatch: string) => { const sp = new URLSearchParams(searchParams) sp.set('url', nextUrl.trim()) if (nextBatch.trim()) sp.set('batch_id', nextBatch.trim()) else sp.delete('batch_id') setSearchParams(sp, { replace: true }) }, [searchParams, setSearchParams], ) const { data, isPending, isError, error, isFetching, } = useQuery({ queryKey: ['lighthouse-report', submittedUrl, submittedBatch || null], queryFn: () => fetchReport(submittedUrl, submittedBatch), staleTime: 60_000, }) function onSubmit(e: React.FormEvent) { e.preventDefault() const u = urlInput.trim() if (u.length < 4) return setSubmittedUrl(u) setSubmittedBatch(batchInput) syncQuery(u, batchInput) } const scores = data?.scores return (
{isPending && (

Loading Lighthouse data…

)} {isError && (
{(error as Error)?.message ?? 'Failed to load report'}
)} {data && scores && (

Values are estimated and may vary. The performance score is calculated directly from these metrics.{' '} See calculator .

Requested URL:
{data.requested_url ?? data.website_url}
{data.final_url && data.final_url !== data.requested_url && (
Final URL:
{data.final_url}
)}
Scanned at:
{data.scanned_at ?? '—'}
{data.lighthouse_version && (
Lighthouse:
{data.lighthouse_version}
)}
{data.screenshot_data_url ? (
Mobile-sized screenshot from the Lighthouse report
Final screenshot (from audit JSON)
) : (
No embedded screenshot in this run (run a full Lighthouse categories audit, not accessibility-only, to capture final-screenshot in stored JSON).
)}
)}
) }