diff --git a/components/dashboard/Sidebar.tsx b/components/dashboard/Sidebar.tsx index 042ac0a..53ccc32 100644 --- a/components/dashboard/Sidebar.tsx +++ b/components/dashboard/Sidebar.tsx @@ -18,6 +18,7 @@ import { SearchIcon, CloudUploadIcon, HeartbeatIcon, + GaugeIcon, SettingsIcon, CollapseLeftIcon, CollapseRightIcon, @@ -88,6 +89,7 @@ const NAV_GROUPS: NavGroup[] = [ items: [ { label: 'CDN', href: (id) => `/sites/${id}/cdn`, icon: CloudUploadIcon, matchPrefix: true }, { label: 'Uptime', href: (id) => `/sites/${id}/uptime`, icon: HeartbeatIcon, matchPrefix: true }, + { label: 'PageSpeed', href: (id) => `/sites/${id}/pagespeed`, icon: GaugeIcon, matchPrefix: true }, ], }, ] diff --git a/lib/api/pagespeed.ts b/lib/api/pagespeed.ts new file mode 100644 index 0000000..ede2fd7 --- /dev/null +++ b/lib/api/pagespeed.ts @@ -0,0 +1,83 @@ +import apiRequest from './client' + +// * Types for PageSpeed Insights monitoring + +export interface PageSpeedConfig { + site_id: string + enabled: boolean + frequency: 'daily' | 'weekly' | 'monthly' + url: string | null + next_check_at: string | null + created_at: string + updated_at: string +} + +export interface AuditSummary { + id: string + title: string + description: string + score: number | null + display_value?: string + savings_ms?: number + category: 'opportunity' | 'diagnostic' | 'passed' +} + +export interface PageSpeedCheck { + id: string + site_id: string + strategy: 'mobile' | 'desktop' + performance_score: number | null + accessibility_score: number | null + best_practices_score: number | null + seo_score: number | null + lcp_ms: number | null + cls: number | null + tbt_ms: number | null + fcp_ms: number | null + si_ms: number | null + tti_ms: number | null + audits: AuditSummary[] | null + triggered_by: 'scheduled' | 'manual' + checked_at: string +} + +export async function getPageSpeedConfig(siteId: string): Promise { + return apiRequest(`/sites/${siteId}/pagespeed/config`) +} + +export async function updatePageSpeedConfig( + siteId: string, + config: { enabled: boolean; frequency: string; url?: string } +): Promise { + return apiRequest(`/sites/${siteId}/pagespeed/config`, { + method: 'PUT', + body: JSON.stringify(config), + }) +} + +export async function getPageSpeedLatest(siteId: string): Promise { + const res = await apiRequest<{ checks: PageSpeedCheck[] }>(`/sites/${siteId}/pagespeed/latest`) + return res?.checks ?? [] +} + +export async function getPageSpeedHistory( + siteId: string, + strategy: 'mobile' | 'desktop' = 'mobile', + days = 90 +): Promise { + const res = await apiRequest<{ checks: PageSpeedCheck[] }>( + `/sites/${siteId}/pagespeed/history?strategy=${strategy}&days=${days}` + ) + return res?.checks ?? [] +} + +export async function getPageSpeedCheck(siteId: string, checkId: string): Promise { + return apiRequest(`/sites/${siteId}/pagespeed/checks/${checkId}`) +} + +export async function triggerPageSpeedCheck(siteId: string): Promise { + const res = await apiRequest<{ checks: PageSpeedCheck[] }>(`/sites/${siteId}/pagespeed/check`, { + method: 'POST', + }) + return res?.checks ?? [] +} diff --git a/lib/swr/dashboard.ts b/lib/swr/dashboard.ts index 1962e67..b08feba 100644 --- a/lib/swr/dashboard.ts +++ b/lib/swr/dashboard.ts @@ -31,6 +31,7 @@ import { getSite } from '@/lib/api/sites' import type { Site } from '@/lib/api/sites' import { listFunnels, type Funnel } from '@/lib/api/funnels' import { getUptimeStatus, type UptimeStatusResponse } from '@/lib/api/uptime' +import { getPageSpeedConfig, getPageSpeedLatest, getPageSpeedHistory, type PageSpeedConfig, type PageSpeedCheck } from '@/lib/api/pagespeed' import { listGoals, type Goal } from '@/lib/api/goals' import { listReportSchedules, listAlertSchedules, type ReportSchedule } from '@/lib/api/report-schedules' import { listSessions, getBotFilterStats, type SessionSummary, type BotFilterStats } from '@/lib/api/bot-filter' @@ -79,6 +80,9 @@ const fetchers = { getJourneyEntryPoints(siteId, start, end), funnels: (siteId: string) => listFunnels(siteId), uptimeStatus: (siteId: string) => getUptimeStatus(siteId), + pageSpeedConfig: (siteId: string) => getPageSpeedConfig(siteId), + pageSpeedLatest: (siteId: string) => getPageSpeedLatest(siteId), + pageSpeedHistory: (siteId: string, strategy: 'mobile' | 'desktop', days: number) => getPageSpeedHistory(siteId, strategy, days), goals: (siteId: string) => listGoals(siteId), reportSchedules: (siteId: string) => listReportSchedules(siteId), alertSchedules: (siteId: string) => listAlertSchedules(siteId), @@ -550,5 +554,32 @@ export function useBotFilterStats(siteId: string) { ) } +// * Hook for PageSpeed config +export function usePageSpeedConfig(siteId: string) { + return useSWR( + siteId ? ['pageSpeedConfig', siteId] : null, + () => fetchers.pageSpeedConfig(siteId), + { ...dashboardSWRConfig, refreshInterval: 0, dedupingInterval: 10 * 1000 } + ) +} + +// * Hook for latest PageSpeed checks (mobile + desktop) +export function usePageSpeedLatest(siteId: string) { + return useSWR( + siteId ? ['pageSpeedLatest', siteId] : null, + () => fetchers.pageSpeedLatest(siteId), + { ...dashboardSWRConfig, refreshInterval: 60 * 1000, dedupingInterval: 10 * 1000, keepPreviousData: true } + ) +} + +// * Hook for PageSpeed score history (trend chart) +export function usePageSpeedHistory(siteId: string, strategy: 'mobile' | 'desktop', days = 90) { + return useSWR( + siteId ? ['pageSpeedHistory', siteId, strategy, days] : null, + () => fetchers.pageSpeedHistory(siteId, strategy, days), + { ...dashboardSWRConfig, refreshInterval: 60 * 1000, dedupingInterval: 10 * 1000, keepPreviousData: true } + ) +} + // * Re-export for convenience export { fetchers }