From 882e91f6a780ddc9de1ff0e83c4379f7ca6c409f Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Sat, 21 Feb 2026 20:31:56 +0000 Subject: [PATCH 1/3] hook up header --- client/src/Pages/PageSpeed/Details/index.tsx | 23 ++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/client/src/Pages/PageSpeed/Details/index.tsx b/client/src/Pages/PageSpeed/Details/index.tsx index f67ce6730..f7b647e15 100644 --- a/client/src/Pages/PageSpeed/Details/index.tsx +++ b/client/src/Pages/PageSpeed/Details/index.tsx @@ -1,4 +1,5 @@ import { BasePage } from "@/Components/design-elements"; +import { HeaderTimeRange } from "@/Components/common"; import type { PageSpeedDetailsResponse } from "@/Types/Monitor"; import Stack from "@mui/material/Stack"; import { @@ -13,18 +14,30 @@ import { useIsAdmin } from "@/Hooks/useIsAdmin"; import { useGet } from "@/Hooks/UseApi"; import { useParams } from "react-router-dom"; import { useTheme } from "@mui/material"; +import { useState, useMemo } from "react"; const PageSpeedDetails = () => { const { monitorId } = useParams(); const isAdmin = useIsAdmin(); const theme = useTheme(); + const [dateRange, setDateRange] = useState("day"); + + const monitorDetailsUrl = useMemo(() => { + if (!monitorId) { + return null; + } + const params = new URLSearchParams(); + params.append("dateRange", dateRange); + return `/monitors/pagespeed/details/${monitorId}?${params.toString()}`; + }, [monitorId, dateRange]); + const { data: monitorData, isLoading, error, refetch, } = useGet( - monitorId ? `/monitors/pagespeed/details/${monitorId}?dateRange=day` : null, + monitorDetailsUrl, {}, { keepPreviousData: true, refreshInterval: 30000 } ); @@ -47,9 +60,15 @@ const PageSpeedDetails = () => { monitor={monitor} monitorStats={monitorStats} /> + Date: Sat, 21 Feb 2026 20:52:47 +0000 Subject: [PATCH 2/3] aggregate pagespeed --- .../checks/MongoChecksRepistory.ts | 42 +++++++++++++++++-- server/src/service/business/monitorService.ts | 6 +-- server/src/types/check.ts | 11 ++++- server/src/types/monitor.ts | 5 ++- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/server/src/repositories/checks/MongoChecksRepistory.ts b/server/src/repositories/checks/MongoChecksRepistory.ts index bc25420b9..a03472b4c 100644 --- a/server/src/repositories/checks/MongoChecksRepistory.ts +++ b/server/src/repositories/checks/MongoChecksRepistory.ts @@ -378,7 +378,7 @@ class MongoChecksRepository implements IChecksRepository { return this.findHardwareDateRangeChecks(monitorObjectId, startDate, endDate, dateString); } if (options?.type === "pagespeed") { - return this.findPageSpeedDateRangeChecks(monitorObjectId, startDate, endDate); + return this.findPageSpeedDateRangeChecks(monitorObjectId, startDate, endDate, dateString); } return this.findUptimeDateRangeChecks(options?.type ?? "http", monitorObjectId, startDate, endDate, dateString); }; @@ -572,16 +572,50 @@ class MongoChecksRepository implements IChecksRepository { }; }; - private findPageSpeedDateRangeChecks = async (monitorObjectId: mongoose.Types.ObjectId, startDate: Date, endDate: Date) => { + private findPageSpeedDateRangeChecks = async (monitorObjectId: mongoose.Types.ObjectId, startDate: Date, endDate: Date, dateString: string) => { const matchStage = { "metadata.monitorId": monitorObjectId, createdAt: { $gte: startDate, $lte: endDate }, }; - const checks = await CheckModel.find(matchStage).sort({ createdAt: -1 }).limit(25).lean(); + const [result] = await CheckModel.aggregate([ + { $match: matchStage }, + { $sort: { createdAt: 1 } }, + { + $facet: { + groupedChecks: [ + { + $group: { + _id: { + $dateToString: { format: dateString, date: "$createdAt" }, + }, + avgPerformance: { $avg: "$performance" }, + avgAccessibility: { $avg: "$accessibility" }, + avgBestPractices: { $avg: "$bestPractices" }, + avgSeo: { $avg: "$seo" }, + totalChecks: { $sum: 1 }, + }, + }, + { $sort: { _id: 1 } }, + { + $project: { + bucketDate: "$_id", + performance: "$avgPerformance", + accessibility: "$avgAccessibility", + bestPractices: "$avgBestPractices", + seo: "$avgSeo", + totalChecks: 1, + _id: 0, + }, + }, + ], + }, + }, + ]); + return { monitorType: "pagespeed" as const, - checks: this.mapDocuments(checks), + groupedChecks: result?.groupedChecks ?? [], }; }; diff --git a/server/src/service/business/monitorService.ts b/server/src/service/business/monitorService.ts index a0591a74f..2005a4a5d 100644 --- a/server/src/service/business/monitorService.ts +++ b/server/src/service/business/monitorService.ts @@ -308,9 +308,9 @@ export class MonitorService implements IMonitorService { const monitorStats = await this.monitorStatsRepository.findByMonitorId(monitor.id); return { - monitor: { - ...monitor, - checks: checksData.checks, + monitorData: { + monitor, + groupedChecks: checksData.groupedChecks, }, monitorStats, }; diff --git a/server/src/types/check.ts b/server/src/types/check.ts index c02b03099..6c4c26445 100644 --- a/server/src/types/check.ts +++ b/server/src/types/check.ts @@ -124,7 +124,7 @@ export interface ChecksQueryResult { export interface PageSpeedChecksResult { monitorType: "pagespeed"; - checks: Check[]; + groupedChecks: PageSpeedGroupedCheck[]; } export interface HardwareChecksResult { @@ -170,6 +170,15 @@ export interface GroupedCheck { totalChecks: number; } +export interface PageSpeedGroupedCheck { + bucketDate: string; + performance: number; + accessibility: number; + bestPractices: number; + seo: number; + totalChecks: number; +} + export interface UptimeChecksResult { monitorType: Exclude; groupedChecks: GroupedCheck[]; diff --git a/server/src/types/monitor.ts b/server/src/types/monitor.ts index a196aaae6..ac7ca02d0 100644 --- a/server/src/types/monitor.ts +++ b/server/src/types/monitor.ts @@ -125,8 +125,9 @@ export interface HardwareDetailsResult { } export interface PageSpeedDetailsResult { - monitor: Monitor & { - checks: import("./check.js").Check[]; + monitorData: { + monitor: Monitor; + groupedChecks: import("./check.js").PageSpeedGroupedCheck[]; }; monitorStats: import("./monitorStats.js").MonitorStats | null; } From f2c88cfd23e45691d5da45d20379a14e3b94c098 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Sat, 21 Feb 2026 21:05:51 +0000 Subject: [PATCH 3/3] fe --- .../monitors/charts/HistogramPageSpeedDetails.tsx | 6 +++--- client/src/Pages/PageSpeed/Details/index.tsx | 5 +++-- client/src/Types/Check.ts | 9 +++++++++ client/src/Types/Monitor.ts | 6 +++++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/client/src/Components/monitors/charts/HistogramPageSpeedDetails.tsx b/client/src/Components/monitors/charts/HistogramPageSpeedDetails.tsx index 8df491197..f89994437 100644 --- a/client/src/Components/monitors/charts/HistogramPageSpeedDetails.tsx +++ b/client/src/Components/monitors/charts/HistogramPageSpeedDetails.tsx @@ -12,7 +12,7 @@ import { import { HistogramPageSpeedScoresTooltip } from "@/Components/monitors"; import { useTheme } from "@mui/material/styles"; -import type { CheckSnapshot } from "@/Types/Check"; +import type { PageSpeedGroupedCheck } from "@/Types/Check"; import type { Palette } from "@mui/material/styles"; type PaletteColorKey = Extract< keyof Palette, @@ -52,7 +52,7 @@ export const HistogramPageSpeedDetails = ({ checks, range, }: { - checks: CheckSnapshot[]; + checks: PageSpeedGroupedCheck[]; range: string; }) => { const theme = useTheme(); @@ -73,7 +73,7 @@ export const HistogramPageSpeedDetails = ({ > ( { { keepPreviousData: true, refreshInterval: 30000 } ); - const monitor = monitorData?.monitor; + const monitor = monitorData?.monitorData?.monitor; + const groupedChecks = monitorData?.monitorData?.groupedChecks || []; const monitorStats = monitorData?.monitorStats || null; return ( @@ -67,7 +68,7 @@ const PageSpeedDetails = () => { setDateRange={setDateRange} />