diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts index 68e2b187b9..70ac67d4b7 100644 --- a/frontend/src/routeTree.gen.ts +++ b/frontend/src/routeTree.gen.ts @@ -16,6 +16,7 @@ import { Route as JoinRouteImport } from './routes/join' import { Route as ContextRouteImport } from './routes/_context' import { Route as ContextIndexRouteImport } from './routes/_context/index' import { Route as OnboardingChooseOrganizationRouteImport } from './routes/onboarding/choose-organization' +import { Route as OnboardingAcceptInvitationRouteImport } from './routes/onboarding/accept-invitation' import { Route as ContextEngineRouteImport } from './routes/_context/_engine' import { Route as ContextCloudRouteImport } from './routes/_context/_cloud' import { Route as ContextEngineNsNamespaceRouteImport } from './routes/_context/_engine/ns.$namespace' @@ -65,6 +66,12 @@ const OnboardingChooseOrganizationRoute = path: '/choose-organization', getParentRoute: () => OnboardingRoute, } as any) +const OnboardingAcceptInvitationRoute = + OnboardingAcceptInvitationRouteImport.update({ + id: '/accept-invitation', + path: '/accept-invitation', + getParentRoute: () => OnboardingRoute, + } as any) const ContextEngineRoute = ContextEngineRouteImport.update({ id: '/_engine', getParentRoute: () => ContextRoute, @@ -151,6 +158,7 @@ export interface FileRoutesByFullPath { '/login': typeof LoginRoute '/onboarding': typeof OnboardingRouteWithChildren '/sso-callback': typeof SsoCallbackRoute + '/onboarding/accept-invitation': typeof OnboardingAcceptInvitationRoute '/onboarding/choose-organization': typeof OnboardingChooseOrganizationRoute '/': typeof ContextIndexRoute '/orgs/$organization': typeof ContextCloudOrgsOrganizationRouteWithChildren @@ -170,6 +178,7 @@ export interface FileRoutesByTo { '/login': typeof LoginRoute '/onboarding': typeof OnboardingRouteWithChildren '/sso-callback': typeof SsoCallbackRoute + '/onboarding/accept-invitation': typeof OnboardingAcceptInvitationRoute '/onboarding/choose-organization': typeof OnboardingChooseOrganizationRoute '/': typeof ContextIndexRoute '/ns/$namespace/connect': typeof ContextEngineNsNamespaceConnectRoute @@ -189,6 +198,7 @@ export interface FileRoutesById { '/sso-callback': typeof SsoCallbackRoute '/_context/_cloud': typeof ContextCloudRouteWithChildren '/_context/_engine': typeof ContextEngineRouteWithChildren + '/onboarding/accept-invitation': typeof OnboardingAcceptInvitationRoute '/onboarding/choose-organization': typeof OnboardingChooseOrganizationRoute '/_context/': typeof ContextIndexRoute '/_context/_cloud/orgs/$organization': typeof ContextCloudOrgsOrganizationRouteWithChildren @@ -210,6 +220,7 @@ export interface FileRouteTypes { | '/login' | '/onboarding' | '/sso-callback' + | '/onboarding/accept-invitation' | '/onboarding/choose-organization' | '/' | '/orgs/$organization' @@ -229,6 +240,7 @@ export interface FileRouteTypes { | '/login' | '/onboarding' | '/sso-callback' + | '/onboarding/accept-invitation' | '/onboarding/choose-organization' | '/' | '/ns/$namespace/connect' @@ -247,6 +259,7 @@ export interface FileRouteTypes { | '/sso-callback' | '/_context/_cloud' | '/_context/_engine' + | '/onboarding/accept-invitation' | '/onboarding/choose-organization' | '/_context/' | '/_context/_cloud/orgs/$organization' @@ -321,6 +334,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof OnboardingChooseOrganizationRouteImport parentRoute: typeof OnboardingRoute } + '/onboarding/accept-invitation': { + id: '/onboarding/accept-invitation' + path: '/accept-invitation' + fullPath: '/onboarding/accept-invitation' + preLoaderRoute: typeof OnboardingAcceptInvitationRouteImport + parentRoute: typeof OnboardingRoute + } '/_context/_engine': { id: '/_context/_engine' path: '' @@ -529,10 +549,12 @@ const ContextRouteWithChildren = ContextRoute._addFileChildren(ContextRouteChildren) interface OnboardingRouteChildren { + OnboardingAcceptInvitationRoute: typeof OnboardingAcceptInvitationRoute OnboardingChooseOrganizationRoute: typeof OnboardingChooseOrganizationRoute } const OnboardingRouteChildren: OnboardingRouteChildren = { + OnboardingAcceptInvitationRoute: OnboardingAcceptInvitationRoute, OnboardingChooseOrganizationRoute: OnboardingChooseOrganizationRoute, } diff --git a/frontend/src/routes/_context.tsx b/frontend/src/routes/_context.tsx index 5d9411cd75..81e2ebd4e1 100644 --- a/frontend/src/routes/_context.tsx +++ b/frontend/src/routes/_context.tsx @@ -33,6 +33,9 @@ const searchSchema = z n: z.array(z.string()).optional(), u: z.string().optional(), t: z.string().optional(), + // clerk related + __clerk_ticket: z.string().optional(), + __clerk_status: z.string().optional(), }) .and(z.record(z.string(), z.any())); @@ -67,6 +70,17 @@ export const Route = createFileRoute("/_context")({ return await match(route.context) .with({ __type: "cloud" }, () => async () => { await waitForClerk(route.context.clerk); + + if ( + route.location.search.__clerk_ticket && + route.location.search.__clerk_status + ) { + throw redirect({ + to: "/onboarding/accept-invitation", + search: { ...route.location.search }, + }); + } + if (!route.context.clerk.user) { throw redirect({ to: "/login", diff --git a/frontend/src/routes/onboarding/accept-invitation.tsx b/frontend/src/routes/onboarding/accept-invitation.tsx new file mode 100644 index 0000000000..bf65d227ca --- /dev/null +++ b/frontend/src/routes/onboarding/accept-invitation.tsx @@ -0,0 +1,213 @@ +import { isClerkAPIResponseError } from "@clerk/clerk-js"; +import { useOrganization, useSignIn, useSignUp } from "@clerk/clerk-react"; +import { createFileRoute, Link, useNavigate } from "@tanstack/react-router"; +import { useEffect, useState } from "react"; +import { useIsMounted } from "usehooks-ts"; +import * as OrgSignUpForm from "@/app/forms/org-sign-up-form"; +import { Logo } from "@/app/logo"; +import { + Button, + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, + toast, +} from "@/components"; + +export const Route = createFileRoute("/onboarding/accept-invitation")({ + component: RouteComponent, +}); + +function RouteComponent() { + const search = Route.useSearch(); + + if (search.__clerk_status === "sign_up") { + // display sign up flow + return ( +