-
-
-
- Paste your deployment endpoint
-
-
-
-
- "https://your-vercel-deployment.vercel.app",
- )
- .with(
- "railway",
- () => "https://your-app.up.railway.app",
- )
- .otherwise(
- () => "https://your-deployment.com",
- )}
- />
-
-
-
+
+
+
+
+
Set environment variables
+
+ Set the following environment variables on the machine
+ running your runner.
+
+
+
- );
- }
+ >
+ );
+}
- return
;
+function useServerfullEndpoint() {
+ const datacenter = useWatch({ name: "datacenter" });
+ const dataProvider = useEngineCompatDataProvider();
+ const { data } = useQuery(
+ dataProvider.datacenterQueryOptions(datacenter || "auto"),
+ );
+ return data?.url || engineEnv().VITE_APP_API_URL;
}
function FrontendSetup() {
diff --git a/frontend/src/components/lib/utils.ts b/frontend/src/components/lib/utils.ts
index 601cdca729..e30db6ac4b 100644
--- a/frontend/src/components/lib/utils.ts
+++ b/frontend/src/components/lib/utils.ts
@@ -69,6 +69,17 @@ export const ls = {
)}`,
);
},
+ skipWelcomeEngine: (ns: string) => {
+ ls.set(
+ `onboarding-skip-welcome-engine-${btoa(JSON.stringify({ ns }))}`,
+ true,
+ );
+ },
+ getSkipWelcomeEngine: (ns: string) => {
+ return ls.get(
+ `onboarding-skip-welcome-engine-${btoa(JSON.stringify({ ns }))}`,
+ );
+ },
},
actorsEphemeralFilters: {
key: "actors-ephemeral-filters",
diff --git a/frontend/src/routes/__root.tsx b/frontend/src/routes/__root.tsx
index c0bfbb6004..3ae317ec9d 100644
--- a/frontend/src/routes/__root.tsx
+++ b/frontend/src/routes/__root.tsx
@@ -29,6 +29,7 @@ function CloudRoute() {
return (
<>
+
{import.meta.env.DEV ? (
) : null}
diff --git a/frontend/src/routes/_context/ns.$namespace.tsx b/frontend/src/routes/_context/ns.$namespace.tsx
index 04455f9ac5..a507766a59 100644
--- a/frontend/src/routes/_context/ns.$namespace.tsx
+++ b/frontend/src/routes/_context/ns.$namespace.tsx
@@ -1,8 +1,13 @@
-import { createFileRoute, useNavigate } from "@tanstack/react-router";
+import { createFileRoute, redirect, useNavigate } from "@tanstack/react-router";
import { match } from "ts-pattern";
+import { GettingStarted } from "@/app/getting-started";
+import { SidebarlessHeader } from "@/app/layout";
import { NotFoundCard } from "@/app/not-found-card";
import { RouteLayout } from "@/app/route-layout";
import { useDialog } from "@/app/use-dialog";
+import { ls } from "@/components";
+import { deriveProviderFromMetadata } from "@/lib/data";
+import { posthog } from "@/lib/posthog";
import {
RECENT_NAMESPACES_KEY,
recordRecentVisit,
@@ -20,15 +25,118 @@ export const Route = createFileRoute("/_context/ns/$namespace")({
.otherwise(() => {
throw new Error("Invalid context type for this route");
}),
- beforeLoad: ({ params }) => {
+ beforeLoad: ({ params, search }) => {
recordRecentVisit(RECENT_NAMESPACES_KEY, params.namespace);
+
+ const s = search as unknown as {
+ skipOnboarding?: boolean;
+ onboardingSuccess?: boolean;
+ };
+ if (s.skipOnboarding) {
+ ls.onboarding.skipWelcomeEngine(params.namespace);
+ posthog.capture("onboarding_skipped", {
+ namespace: params.namespace,
+ });
+ throw redirect({ to: ".", search: {} });
+ }
+ if (s.onboardingSuccess) {
+ throw redirect({ to: ".", search: {} });
+ }
+ },
+ loaderDeps(opts) {
+ const s = opts.search as unknown as {
+ skipOnboarding?: boolean;
+ backendOnboardingSuccess?: boolean;
+ onboardingSuccess?: boolean;
+ };
+ return {
+ skipOnboarding: s.skipOnboarding,
+ backendOnboardingSuccess: s.backendOnboardingSuccess,
+ onboardingSuccess: s.onboardingSuccess,
+ };
+ },
+ async loader({ params, deps, context }) {
+ const d = deps as {
+ skipOnboarding?: boolean;
+ backendOnboardingSuccess?: boolean;
+ onboardingSuccess?: boolean;
+ };
+ const isSkipped =
+ ls.onboarding.getSkipWelcomeEngine(params.namespace) ||
+ d.skipOnboarding;
+
+ if (isSkipped === true) {
+ return {
+ dataProvider: context.dataProvider,
+ displayOnboarding: false,
+ displayFrontendOnboarding: false,
+ };
+ }
+
+ const [runnerNames, runnerConfigs] = await Promise.all([
+ context.queryClient.fetchInfiniteQuery(
+ context.dataProvider.runnerNamesQueryOptions(),
+ ),
+ context.queryClient.fetchInfiniteQuery(
+ context.dataProvider.runnerConfigsQueryOptions(),
+ ),
+ ]);
+
+ const runnerProvider = runnerConfigs.pages
+ .flatMap((page) =>
+ Object.values(page.runnerConfigs).flatMap((config) =>
+ Object.values(config.datacenters).map((dc) =>
+ deriveProviderFromMetadata(dc.metadata),
+ ),
+ ),
+ )
+ .find((provider) => provider !== undefined);
+
+ const actors = await context.queryClient.fetchQuery(
+ context.dataProvider.actorsCountQueryOptions(),
+ );
+
+ const hasRunnerNames = runnerNames.pages[0].names.length > 0;
+ const hasRunnerConfigs =
+ Object.entries(runnerConfigs.pages[0].runnerConfigs).length > 0;
+ const hasActors = actors > 0;
+
+ const hasBackendConfigured = hasRunnerNames || hasRunnerConfigs;
+
+ return {
+ dataProvider: context.dataProvider,
+ displayOnboarding: !hasBackendConfigured && !hasActors,
+ displayFrontendOnboarding: hasBackendConfigured && !hasActors,
+ provider: runnerProvider,
+ };
},
- loader: ({ context }) => ({ dataProvider: context.dataProvider }),
component: RouteComponent,
notFoundComponent: () =>
,
});
function RouteComponent() {
+ const {
+ displayOnboarding,
+ displayFrontendOnboarding,
+ provider: runnerProvider,
+ } = Route.useLoaderData();
+ const { provider } = Route.useSearch();
+ const { namespace } = Route.useParams();
+
+ if (displayOnboarding || displayFrontendOnboarding) {
+ return (
+ <>
+
+
+
+ >
+ );
+ }
+
return (
<>