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.
This commit is contained in:
Usman Baig
2026-03-22 18:35:17 +01:00
parent d1af25266b
commit 2fd9bf82f1
2 changed files with 41 additions and 18 deletions

View File

@@ -1,7 +1,7 @@
'use client' 'use client'
import { useAuth } from '@/lib/auth/context' import { useAuth } from '@/lib/auth/context'
import { useEffect, useState } from 'react' import { useEffect, useState, useRef, useCallback } from 'react'
import { useParams } from 'next/navigation' import { useParams } from 'next/navigation'
import { useSite, usePageSpeedConfig, usePageSpeedLatest, usePageSpeedHistory } from '@/lib/swr/dashboard' import { useSite, usePageSpeedConfig, usePageSpeedLatest, usePageSpeedHistory } from '@/lib/swr/dashboard'
import { updatePageSpeedConfig, triggerPageSpeedCheck, type PageSpeedCheck, type AuditSummary } from '@/lib/api/pagespeed' import { updatePageSpeedConfig, triggerPageSpeedCheck, type PageSpeedCheck, type AuditSummary } from '@/lib/api/pagespeed'
@@ -116,15 +116,47 @@ export default function PageSpeedPage() {
} }
// * Trigger a manual PageSpeed check // * Trigger a manual PageSpeed check
// * Poll for results after triggering an async check
const pollRef = useRef<ReturnType<typeof setInterval> | 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 () => { const handleRunCheck = async () => {
setRunning(true) setRunning(true)
try { try {
await triggerPageSpeedCheck(siteId) await triggerPageSpeedCheck(siteId)
mutateLatest() toast.success('PageSpeed check started — results will appear in 30-60 seconds')
toast.success('PageSpeed check complete')
// * 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) { } catch (err: any) {
toast.error(err?.message || 'Failed to run check') toast.error(err?.message || 'Failed to start check')
} finally {
setRunning(false) setRunning(false)
} }
} }

View File

@@ -75,17 +75,8 @@ export async function getPageSpeedCheck(siteId: string, checkId: string): Promis
return apiRequest<PageSpeedCheck>(`/sites/${siteId}/pagespeed/checks/${checkId}`) return apiRequest<PageSpeedCheck>(`/sites/${siteId}/pagespeed/checks/${checkId}`)
} }
export async function triggerPageSpeedCheck(siteId: string): Promise<PageSpeedCheck[]> { // * Triggers an async PageSpeed check. Returns immediately (202).
// * PSI checks take 10-30s per strategy (mobile + desktop sequential = up to 60s) // * Caller should poll getPageSpeedLatest() for results.
const controller = new AbortController() export async function triggerPageSpeedCheck(siteId: string): Promise<void> {
const timeoutId = setTimeout(() => controller.abort(), 120_000) await apiRequest(`/sites/${siteId}/pagespeed/check`, { method: 'POST' })
try {
const res = await apiRequest<{ checks: PageSpeedCheck[] }>(`/sites/${siteId}/pagespeed/check`, {
method: 'POST',
signal: controller.signal,
})
return res?.checks ?? []
} finally {
clearTimeout(timeoutId)
}
} }