From bddb4c0d16a7008281d31cd1b09f01ab7bbccc56 Mon Sep 17 00:00:00 2001 From: Will Washburn Date: Sun, 20 Sep 2026 01:57:17 -0700 Subject: [PATCH] feat(flows): hand onboarding off to Cloud --- AGENTS.md | 2 +- web/app/flows/FlowLandingAnalytics.tsx | 2 +- web/app/flows/onboarding/[stage]/page.tsx | 17 --------------- web/app/flows/onboarding/page.tsx | 11 ---------- web/app/flows/page.tsx | 11 ++++------ web/lib/flows-cloud.ts | 11 ++++++++++ web/lib/test/flows-cloud.test.ts | 25 +++++++++++++++++++++++ 7 files changed, 42 insertions(+), 37 deletions(-) delete mode 100644 web/app/flows/onboarding/[stage]/page.tsx delete mode 100644 web/app/flows/onboarding/page.tsx create mode 100644 web/lib/flows-cloud.ts create mode 100644 web/lib/test/flows-cloud.test.ts diff --git a/AGENTS.md b/AGENTS.md index 3e8c919..d89b540 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -42,7 +42,7 @@ These rules are repository law for every agent session in this checkout. ## Working discipline - For paired local Flows onboarding, Google sign-in, and dashboard tests, use - `npm run dev:flows` from the sibling `../cloud` repo root. Cloud owns the + `npm run dev:onboarding` from the sibling `../cloud` repo root. Cloud owns the paired launcher; read `../cloud/README.md`'s Local Setup section first. Do not start the two Next apps separately or use `dev:teams` for the Flows journey. Use the printed local URL exactly. Identify occupied ports and their owning diff --git a/web/app/flows/FlowLandingAnalytics.tsx b/web/app/flows/FlowLandingAnalytics.tsx index a42a8ca..94ad1cb 100644 --- a/web/app/flows/FlowLandingAnalytics.tsx +++ b/web/app/flows/FlowLandingAnalytics.tsx @@ -18,7 +18,7 @@ export function FlowLandingAnalytics() { const link = (event.target as Element).closest?.('a[href]'); if (!(link instanceof HTMLAnchorElement)) return; const url = new URL(link.href); - if (url.origin !== window.location.origin || url.pathname !== '/flows/onboarding') return; + if (!url.pathname.endsWith('/api/auth/google/start') || url.searchParams.get('source') !== 'flows') return; const placement = url.searchParams.get('utm_content'); capture('flows_onboarding_entry_clicked', { placement: ['hero', 'nav', 'mobile_nav'].includes(placement ?? '') ? placement : 'other' }); }; diff --git a/web/app/flows/onboarding/[stage]/page.tsx b/web/app/flows/onboarding/[stage]/page.tsx deleted file mode 100644 index ba32301..0000000 --- a/web/app/flows/onboarding/[stage]/page.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { notFound, redirect } from 'next/navigation'; -import { ONBOARDING_STAGES } from '../../../../lib/flow-onboarding'; - -export { metadata } from '../page'; - -export default async function OnboardingStagePage({ params, searchParams }: { params: Promise<{ stage: string }>; searchParams: Promise> }) { - const { stage } = await params; - if (['reviewer', 'reviews', 'approval'].includes(stage)) { - const query = new URLSearchParams(); - for (const [key, value] of Object.entries(await searchParams)) { - for (const item of Array.isArray(value) ? value : value === undefined ? [] : [value]) query.append(key, item); - } - redirect('/flows/onboarding/task' + (query.size ? '?' + query : '')); - } - if (!ONBOARDING_STAGES.some(value => value === stage)) notFound(); - return null; -} diff --git a/web/app/flows/onboarding/page.tsx b/web/app/flows/onboarding/page.tsx deleted file mode 100644 index 19e9e23..0000000 --- a/web/app/flows/onboarding/page.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import type { Metadata } from 'next'; - -export const metadata: Metadata = { - title: 'Create your first software factory', - description: 'Choose your coding agent and build a flow that opens PRs, reviews its work, and waits for your approval.', - robots: { index: false, follow: true }, -}; - -export default function FlowOnboardingPage() { - return null; -} diff --git a/web/app/flows/page.tsx b/web/app/flows/page.tsx index 11e3e02..673ec20 100644 --- a/web/app/flows/page.tsx +++ b/web/app/flows/page.tsx @@ -13,6 +13,7 @@ import { SiteFooter } from '../../components/SiteFooter'; import { SiteNav } from '../../components/SiteNav'; import { FLOWS_OG_ALT, FLOWS_OG_IMAGE_PATH, ogImage } from '../../lib/og-meta'; import { absoluteUrl, SITE_NAME } from '../../lib/site'; +import { flowsGoogleAuthHref } from '../../lib/flows-cloud'; import home from '../landing.module.css'; import s from './flows.module.css'; import { FlowExamples } from './FlowExamples'; @@ -42,10 +43,6 @@ export const metadata: Metadata = { }, }; -function flowsSignInHref(utmContent: string) { - return `/flows/onboarding?ref=flows&utm_source=agentrelay.com&utm_medium=flows_landing&utm_campaign=flows&utm_content=${utmContent}`; -} - function WaveBreak({ tone = 'blue' }: { tone?: 'blue' | 'orange' }) { return (