From 2fd9bf82f121e6ebf53afcc988cdb2c90503277c Mon Sep 17 00:00:00 2001 From: Usman Baig Date: Sun, 22 Mar 2026 18:35:17 +0100 Subject: [PATCH] fix(pagespeed): poll for results after async check trigger Backend now returns 202 immediately. Frontend polls every 5s for up to 2 minutes until new results appear, then shows success toast. --- app/sites/[id]/pagespeed/page.tsx | 42 +++++++++++++++++++++++++++---- lib/api/pagespeed.ts | 17 +++---------- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/app/sites/[id]/pagespeed/page.tsx b/app/sites/[id]/pagespeed/page.tsx index cca1fdf..6d7c894 100644 --- a/app/sites/[id]/pagespeed/page.tsx +++ b/app/sites/[id]/pagespeed/page.tsx @@ -1,7 +1,7 @@ 'use client' import { useAuth } from '@/lib/auth/context' -import { useEffect, useState } from 'react' +import { useEffect, useState, useRef, useCallback } from 'react' import { useParams } from 'next/navigation' import { useSite, usePageSpeedConfig, usePageSpeedLatest, usePageSpeedHistory } from '@/lib/swr/dashboard' import { updatePageSpeedConfig, triggerPageSpeedCheck, type PageSpeedCheck, type AuditSummary } from '@/lib/api/pagespeed' @@ -116,15 +116,47 @@ export default function PageSpeedPage() { } // * Trigger a manual PageSpeed check + // * Poll for results after triggering an async check + const pollRef = useRef | null>(null) + const stopPolling = useCallback(() => { + if (pollRef.current) { + clearInterval(pollRef.current) + pollRef.current = null + } + }, []) + + // * Clean up polling on unmount + useEffect(() => () => stopPolling(), [stopPolling]) + const handleRunCheck = async () => { setRunning(true) try { await triggerPageSpeedCheck(siteId) - mutateLatest() - toast.success('PageSpeed check complete') + toast.success('PageSpeed check started — results will appear in 30-60 seconds') + + // * Poll every 5s for up to 2 minutes until new results appear + const startedAt = Date.now() + const initialCheckedAt = latestChecks?.[0]?.checked_at + + stopPolling() + pollRef.current = setInterval(async () => { + const elapsed = Date.now() - startedAt + if (elapsed > 120_000) { + stopPolling() + setRunning(false) + toast.error('Check is taking longer than expected. Results will appear when ready.') + return + } + const freshData = await mutateLatest() + const freshCheckedAt = freshData?.[0]?.checked_at + if (freshCheckedAt && freshCheckedAt !== initialCheckedAt) { + stopPolling() + setRunning(false) + toast.success('PageSpeed check complete') + } + }, 5000) } catch (err: any) { - toast.error(err?.message || 'Failed to run check') - } finally { + toast.error(err?.message || 'Failed to start check') setRunning(false) } } diff --git a/lib/api/pagespeed.ts b/lib/api/pagespeed.ts index c67f8a7..5650a9c 100644 --- a/lib/api/pagespeed.ts +++ b/lib/api/pagespeed.ts @@ -75,17 +75,8 @@ export async function getPageSpeedCheck(siteId: string, checkId: string): Promis return apiRequest(`/sites/${siteId}/pagespeed/checks/${checkId}`) } -export async function triggerPageSpeedCheck(siteId: string): Promise { - // * PSI checks take 10-30s per strategy (mobile + desktop sequential = up to 60s) - const controller = new AbortController() - const timeoutId = setTimeout(() => controller.abort(), 120_000) - try { - const res = await apiRequest<{ checks: PageSpeedCheck[] }>(`/sites/${siteId}/pagespeed/check`, { - method: 'POST', - signal: controller.signal, - }) - return res?.checks ?? [] - } finally { - clearTimeout(timeoutId) - } +// * Triggers an async PageSpeed check. Returns immediately (202). +// * Caller should poll getPageSpeedLatest() for results. +export async function triggerPageSpeedCheck(siteId: string): Promise { + await apiRequest(`/sites/${siteId}/pagespeed/check`, { method: 'POST' }) }