import { createFileRoute, useNavigate } from "@tanstack/react-router"; import { useCallback } from "react"; import { AlertCircle, Loader2 } from "lucide-react"; import { useQuery } from "@tanstack/react-query"; import { getAuditResults, getAuditStatus, getCrawlProgress, } from "@/serverFunctions/audit"; import { auditSearchSchema } from "@/types/schemas/audit"; import { LaunchView } from "@/client/features/audit/launch/LaunchView"; import { ResultsView } from "@/client/features/audit/results/ResultsView"; import { extractHostname, extractPathname, formatStartedAt, HttpStatusBadge, StatusBadge, SUPPORT_URL, } from "@/client/features/audit/shared"; export const Route = createFileRoute<"/_project/p/$projectId/audit/">( "/_project/p/$projectId/audit/", )({ validateSearch: auditSearchSchema, component: SiteAuditPage, }); function SiteAuditPage() { const { projectId } = Route.useParams(); const { auditId, tab } = Route.useSearch(); const navigate = useNavigate({ from: Route.fullPath }); const setSearchParams = useCallback( (updates: Record) => { void navigate({ search: (prev) => ({ ...prev, ...updates }), replace: true, }); }, [navigate], ); if (!auditId) { return ( setSearchParams({ auditId: id })} /> ); } return ( setSearchParams({ auditId: undefined })} /> ); } function AuditDetail({ projectId, auditId, tab, onBack, }: { projectId: string; auditId: string; tab: string; onBack: () => void; }) { const statusQuery = useQuery({ queryKey: ["audit-status", projectId, auditId], queryFn: () => getAuditStatus({ data: { projectId, auditId } }), refetchInterval: (query) => { const data = query.state.data; return data?.status === "running" ? 3000 : false; }, }); const isComplete = statusQuery.data?.status === "completed"; const isFailed = statusQuery.data?.status === "failed"; const isRunning = statusQuery.data?.status === "running"; const resultsQuery = useQuery({ queryKey: ["audit-results", projectId, auditId], queryFn: () => getAuditResults({ data: { projectId, auditId } }), enabled: isComplete, }); if (statusQuery.isLoading) { return (
); } if (statusQuery.isError) { return (
We could not load this audit. It may have been deleted.
); } const status = statusQuery.data; const showSupportCta = isFailed || (isComplete && status && status.pagesCrawled <= 1); return (

Site Audit

{status?.status !== "running" && status && ( )}
{status && (

{extractHostname(status.startUrl)} · Started{" "} {formatStartedAt(status.startedAt)}

)}
{isRunning && status && ( )} {showSupportCta && (

Site audit couldn't fully crawl this website.

This is often caused by anti-bot or firewall settings. Reach out at{" "} everyapp.dev/support {" "} and we'll help configure auditing for your site.

)} {isComplete && resultsQuery.data && ( )}
); } function ProgressCard({ projectId, auditId, status, }: { projectId: string; auditId: string; status: { pagesCrawled: number; pagesTotal: number; lighthouseTotal: number; lighthouseCompleted: number; lighthouseFailed: number; currentPhase: string | null; }; }) { const crawlProgress = status.pagesTotal > 0 ? Math.round((status.pagesCrawled / status.pagesTotal) * 100) : 0; const lighthouseDone = status.lighthouseCompleted + status.lighthouseFailed; const lighthouseProgress = status.lighthouseTotal > 0 ? Math.round((lighthouseDone / status.lighthouseTotal) * 100) : 0; const isLighthousePhase = status.currentPhase === "lighthouse"; const phaseLabel = status.currentPhase === "discovery" ? "Discovery" : status.currentPhase === "crawling" ? "Crawling" : status.currentPhase === "lighthouse" ? "Lighthouse" : status.currentPhase === "finalizing" ? "Finalizing" : (status.currentPhase ?? "Running"); const progress = isLighthousePhase ? lighthouseProgress : crawlProgress; const crawlProgressQuery = useQuery({ queryKey: ["audit-crawl-progress", projectId, auditId], queryFn: () => getCrawlProgress({ data: { projectId, auditId } }), refetchInterval: 1500, }); const crawledUrls = crawlProgressQuery.data ?? []; return (

{isLighthousePhase ? "Running Lighthouse checks" : "Crawling pages"}

{phaseLabel}
{isLighthousePhase ? ( {lighthouseDone} / {status.lighthouseTotal} checks {status.lighthouseFailed > 0 ? ` (${status.lighthouseFailed} failed)` : ""} ) : ( {status.pagesCrawled} / {status.pagesTotal} pages )} {progress}%
{crawledUrls.length > 0 && (

Crawled Pages ({crawledUrls.length})

Updated {new Date(crawledUrls[0].crawledAt).toLocaleTimeString()}

{crawledUrls.map((entry, i) => ( ))}
)}
); } function ProgressRow({ entry, index, }: { entry: { url: string; statusCode: number | null; title: string | null; crawledAt: number; }; index: number; }) { const pathname = extractPathname(entry.url); return (
{pathname}
{entry.title && ( {entry.title} )}
); }