diff --git a/apps/dashboard/src/app/(dashboard)/monitors/(list)/client.tsx b/apps/dashboard/src/app/(dashboard)/monitors/(list)/client.tsx index af68ed689..969521ed9 100644 --- a/apps/dashboard/src/app/(dashboard)/monitors/(list)/client.tsx +++ b/apps/dashboard/src/app/(dashboard)/monitors/(list)/client.tsx @@ -1,5 +1,6 @@ "use client"; +import type { RouterInputs } from "@openstatus/api"; import { ArrowDown, Success, ListFilter } from "@openstatus/icons"; import { useQueries, useQuery } from "@tanstack/react-query"; import type { ColumnFiltersState, SortingState } from "@tanstack/react-table"; @@ -31,6 +32,20 @@ import { useTRPC } from "@/lib/trpc/client"; import { searchParamsParsers } from "./search-params"; +type MetricType = NonNullable< + RouterInputs["tinybird"]["globalMetrics"]["type"] +>; + +// Job types with a tinybird metrics pipe (udp/ssl have none yet). The record +// is keyed by the server enum so a type added there fails typecheck here. +const METRIC_TYPES = Object.keys({ + http: true, + tcp: true, + dns: true, + icmp: true, + grpc: true, +} satisfies Record) as MetricType[]; + const icons = { default: { active: Success, @@ -50,55 +65,20 @@ export function Client() { const [sorting, setSorting] = useState([]); const [columnFilters, setColumnFilters] = useState([]); - const monitorsByType = { - http: - monitors - ?.filter((m) => m.jobType === "http") - .map((m) => m.id.toString()) ?? [], - tcp: - monitors - ?.filter((m) => m.jobType === "tcp") - .map((m) => m.id.toString()) ?? [], - dns: - monitors - ?.filter((m) => m.jobType === "dns") - .map((m) => m.id.toString()) ?? [], - }; - const { - http: httpMonitors, - tcp: tcpMonitors, - dns: dnsMonitors, - } = monitorsByType; - - const [ - { data: globalHttpMetrics, isLoading: isLoadingHttp }, - { data: globalTcpMetrics, isLoading: isLoadingTcp }, - { data: globalDnsMetrics, isLoading: isLoadingDns }, - ] = useQueries({ - queries: [ - { - ...trpc.tinybird.globalMetrics.queryOptions({ - monitorIds: httpMonitors, - type: "http", - }), - enabled: httpMonitors.length > 0, - }, - { - ...trpc.tinybird.globalMetrics.queryOptions({ - monitorIds: tcpMonitors, - type: "tcp", - }), - enabled: tcpMonitors.length > 0, - }, - { - ...trpc.tinybird.globalMetrics.queryOptions({ - monitorIds: dnsMonitors, - type: "dns", - }), - enabled: dnsMonitors.length > 0, - }, - ], + const metricQueries = useQueries({ + queries: METRIC_TYPES.map((type) => { + const monitorIds = + monitors + ?.filter((m) => m.jobType === type) + .map((m) => m.id.toString()) ?? []; + return { + ...trpc.tinybird.globalMetrics.queryOptions({ monitorIds, type }), + enabled: monitorIds.length > 0, + }; + }), }); + const isLoadingMetrics = metricQueries.some((q) => q.isLoading); + const globalMetrics = metricQueries.flatMap((q) => q.data?.data ?? []); // TODO: ideally we read from the searchParamsCache and there is no layout shift useEffect(() => { @@ -113,11 +93,7 @@ export function Client() { if (!monitors) return null; - const metrics = getMonitorListMetrics(monitors, [ - ...(globalHttpMetrics?.data ?? []), - ...(globalTcpMetrics?.data ?? []), - ...(globalDnsMetrics?.data ?? []), - ]); + const metrics = getMonitorListMetrics(monitors, globalMetrics); return ( @@ -174,8 +150,7 @@ export function Client() { - {metric.key === "p95" && - (isLoadingHttp || isLoadingTcp || isLoadingDns) ? ( + {metric.key === "p95" && isLoadingMetrics ? ( ) : ( {metric.value} @@ -190,20 +165,11 @@ export function Client() { columns={columns} data={monitors.map((monitor) => ({ ...monitor, - globalMetrics: - isLoadingHttp || isLoadingTcp || isLoadingDns - ? undefined - : monitor.jobType === "http" - ? (globalHttpMetrics?.data?.find( - (m) => m.monitorId === monitor.id.toString(), - ) ?? false) - : monitor.jobType === "tcp" - ? (globalTcpMetrics?.data?.find( - (m) => m.monitorId === monitor.id.toString(), - ) ?? false) - : (globalDnsMetrics?.data?.find( - (m) => m.monitorId === monitor.id.toString(), - ) ?? false), + globalMetrics: isLoadingMetrics + ? undefined + : (globalMetrics.find( + (m) => m.monitorId === monitor.id.toString(), + ) ?? false), }))} actionBar={MonitorDataTableActionBar} toolbarComponent={(props) => (