From 03b275f25ea2ea96e63af5051786e038b3291cd9 Mon Sep 17 00:00:00 2001 From: jog1t <39823706+jog1t@users.noreply.github.com> Date: Sat, 17 Jan 2026 03:40:29 +0000 Subject: [PATCH] fix(dashboard): fetch properly actor counts (#3961) ### TL;DR Added actor count query functionality and improved the onboarding experience with better error handling and deployment URL formatting. ### What changed? - Added a new `actorsCountQueryOptions` function to efficiently query the count of actors - Updated the frontend setup to use the new actor count query instead of fetching all actors - Improved actor error display to show crash messages - Fixed deployment URL formatting by properly extracting the base URL - Added Posthog tracking for when users skip onboarding - Enhanced the namespace route to use the new actor count query for determining if actors exist - Improved error handling in the actor status label component to display more specific crash messages ### How to test? 1. Navigate to the getting started flow and verify the actor count is displayed correctly 2. Check that deployment URLs are properly formatted in the frontend setup 3. Trigger an actor crash and verify the error message is displayed correctly 4. Skip onboarding and confirm the Posthog event is captured ### Why make this change? The previous implementation was inefficient as it fetched all actors just to get a count. This change optimizes performance by providing a dedicated endpoint for actor counts. Additionally, the improved error handling and URL formatting enhance the user experience during onboarding by providing clearer feedback and properly formatted links. --- .../data-providers/engine-data-provider.tsx | 35 ++++++++++++++++ frontend/src/app/getting-started.tsx | 40 +++++++++++-------- .../components/actors/actor-status-label.tsx | 4 +- .../projects.$project/ns.$namespace.tsx | 25 +++--------- 4 files changed, 66 insertions(+), 38 deletions(-) diff --git a/frontend/src/app/data-providers/engine-data-provider.tsx b/frontend/src/app/data-providers/engine-data-provider.tsx index 0904ff88c9..7282347db0 100644 --- a/frontend/src/app/data-providers/engine-data-provider.tsx +++ b/frontend/src/app/data-providers/engine-data-provider.tsx @@ -648,6 +648,41 @@ export const createNamespaceContext = ({ }, }); }, + + actorsCountQueryOptions() { + return queryOptions({ + queryKey: [{ namespace }, "actors", "count"] as QueryKey, + enabled: true, + queryFn: async () => { + // TODO: fetch all actor names only to get the count is inefficient + const namesList = await client.actorsListNames({ + namespace, + limit: 100, + }); + + const names = Object.keys(namesList.names); + + const data = await Promise.all( + names.map((name) => + client.actorsList({ + namespace, + name, + limit: 1, + }), + ), + ); + return data.reduce( + (acc, curr) => acc + curr.actors.length, + 0, + ); + }, + retry: shouldRetryAllExpect403, + throwOnError: noThrow, + meta: { + mightRequireAuth, + }, + }); + }, }; }; diff --git a/frontend/src/app/getting-started.tsx b/frontend/src/app/getting-started.tsx index 285781ae84..fb6cc8536e 100644 --- a/frontend/src/app/getting-started.tsx +++ b/frontend/src/app/getting-started.tsx @@ -3,6 +3,7 @@ import { deployOptions } from "@rivetkit/example-registry"; import { useInfiniteQuery, useMutation, + useQuery, useSuspenseInfiniteQuery, } from "@tanstack/react-query"; import { @@ -12,18 +13,17 @@ import { useSearch, } from "@tanstack/react-router"; import { motion } from "framer-motion"; -import { Suspense, useEffect } from "react"; +import posthog from "posthog-js"; +import { Suspense, useEffect, useMemo } from "react"; import { useFormContext, useWatch } from "react-hook-form"; import { match, P } from "ts-pattern"; import z from "zod"; import * as ConnectServerlessForm from "@/app/forms/connect-manual-serverless-form"; import { ButtonCard, - Card, CodeFrame, CodeGroup, CodePreview, - ExternalCard, ExternalLinkCard, FormField, H1, @@ -42,10 +42,7 @@ import { buildServerlessConfig, ConfigurationAccordion, } from "./dialogs/connect-manual-serverless-frame"; -import { - DeployToVercelCard, - StepInitialInfo as VercelQuickSetupInfo, -} from "./dialogs/connect-quick-vercel-frame"; +import { DeployToVercelCard } from "./dialogs/connect-quick-vercel-frame"; import { EnvVariables } from "./env-variables"; import { StepperForm } from "./forms/stepper-form"; import { Content } from "./layout"; @@ -226,6 +223,7 @@ export function GettingStarted({ ...values, endpoint: status.url || values.endpoint, }, + { provider: values.provider }, ); await mutateAsync({ @@ -593,22 +591,19 @@ function FrontendSetup() { maxPages: 1, }); - const { data: actors } = useInfiniteQuery({ - ...dataProvider.actorsQueryOptions({ - filters: { showDestroyed: { value: ["true"] } }, - n: builds?.map((b) => b.id) || [], - }), + const { data: actors } = useQuery({ + ...dataProvider.actorsCountQueryOptions(), enabled: (builds?.length || 0) > 0, maxPages: 1, refetchInterval: 2500, }); - const hasActors = (actors?.pages[0].actors.length || 0) > 0; + const hasActors = (actors || 0) > 0; const navigate = useNavigate(); const router = useRouter(); - const { data } = useInfiniteQuery({ + const { data: config } = useInfiniteQuery({ ...dataProvider.runnerConfigsQueryOptions(), select: (data) => Object.values(data.pages[0].runnerConfigs || {}) @@ -616,11 +611,22 @@ function FrontendSetup() { .filter((dc) => dc.serverless)?.[0].serverless, }); + const deploymentUrl = useMemo(() => { + if (!config?.url) return null; + try { + const url = new URL(config.url); + url.pathname = "/"; + return url.toString(); + } catch { + return null; + } + }, [config?.url]); + useEffect(() => { const success = async () => { successfulBackendSetupEffect(); - await router.invalidate(); + router.invalidate(); return navigate({ to: ".", search: (s) => ({ @@ -647,10 +653,10 @@ function FrontendSetup() {
- {data?.url ? ( + {deploymentUrl ? (