diff --git a/.changeset/remove-legacy-register-flow.md b/.changeset/remove-legacy-register-flow.md new file mode 100644 index 00000000000..7d03565fe72 --- /dev/null +++ b/.changeset/remove-legacy-register-flow.md @@ -0,0 +1,5 @@ +--- +"dashboard": patch +--- + +Removed the legacy session-aware `/register` compatibility flow. Stale links now redirect directly to `/sign-up`. diff --git a/client/dashboard/src/components/page-templates/centered-page.tsx b/client/dashboard/src/components/page-templates/centered-page.tsx index 430e51d3dd8..50dac89d678 100644 --- a/client/dashboard/src/components/page-templates/centered-page.tsx +++ b/client/dashboard/src/components/page-templates/centered-page.tsx @@ -1,6 +1,6 @@ /** * CenteredPage — the shell for full-viewport, standalone pages rendered outside - * the main app layout: auth (login/register/signup), demo redirects, access + * the main app layout: auth (login/signup), demo redirects, access * requests, not-found, policy acknowledgements. * * These already share two shells; this names them as the canonical choice so a diff --git a/client/dashboard/src/pages/login/Register.test.tsx b/client/dashboard/src/pages/login/Register.test.tsx deleted file mode 100644 index caffeea86f0..00000000000 --- a/client/dashboard/src/pages/login/Register.test.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { cleanup, render, screen } from "@testing-library/react"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; - -import Register from "./Register"; - -const mocks = vi.hoisted(() => ({ - useSearchParams: vi.fn(), - useSession: vi.fn(), -})); - -vi.mock("@/contexts/Auth", () => ({ useSession: mocks.useSession })); -vi.mock("react-router", () => ({ - Navigate: ({ to }: { to: string }) => ( -
- ), - useSearchParams: mocks.useSearchParams, -})); -function renderAt(path: string) { - const [, search = ""] = path.split("?"); - mocks.useSearchParams.mockReturnValue([new URLSearchParams(search), vi.fn()]); - render(); -} - -let originalLocation: Location | undefined; - -beforeEach(() => { - originalLocation = window.location; - // @ts-expect-error test-only location replacement for same-origin URL parsing - delete window.location; - Object.defineProperty(window, "location", { - configurable: true, - value: { origin: "https://app.example" }, - }); -}); - -afterEach(() => { - cleanup(); - if (originalLocation) { - Object.defineProperty(window, "location", { - configurable: true, - value: originalLocation, - }); - } - vi.clearAllMocks(); -}); - -describe("Register", () => { - it("redirects logged-out /register traffic to /sign-up", () => { - mocks.useSession.mockReturnValue({ session: "", activeOrganizationId: "" }); - - renderAt("/register?redirect=%2Fcli%2Fcallback"); - - expect(screen.getByTestId("navigate").getAttribute("data-to")).toBe( - "/sign-up?redirect=https%3A%2F%2Fapp.example%2Fcli%2Fcallback", - ); - }); - - it("preserves assistants disposition", () => { - mocks.useSession.mockReturnValue({ session: "", activeOrganizationId: "" }); - - renderAt("/register?disposition=assistants"); - - expect(screen.getByTestId("navigate").getAttribute("data-to")).toBe( - "/login?disposition=assistants", - ); - }); - - it("drops an external legacy redirect", () => { - mocks.useSession.mockReturnValue({ session: "", activeOrganizationId: "" }); - - renderAt("/register?redirect=https%3A%2F%2Fevil.example"); - - expect(screen.getByTestId("navigate").getAttribute("data-to")).toBe( - "/sign-up", - ); - }); - - it("redirects an authenticated session with an organization to the root", () => { - mocks.useSession.mockReturnValue({ - session: "", - activeOrganizationId: "", - }); - - renderAt("/register"); - - expect(screen.getByTestId("navigate").getAttribute("data-to")).toBe("/"); - expect(screen.queryByTestId("register-panel")).toBeNull(); - }); - - it("preserves the path, search, and hash for an authenticated redirect", () => { - mocks.useSession.mockReturnValue({ - session: "", - activeOrganizationId: "", - }); - - renderAt( - "/register?redirect=https%3A%2F%2Fapp.example%2Fprojects%2Fdefault%3Ftab%3Dtools%23details", - ); - - expect(screen.getByTestId("navigate").getAttribute("data-to")).toBe( - "/projects/default?tab=tools#details", - ); - }); -}); diff --git a/client/dashboard/src/pages/login/Register.tsx b/client/dashboard/src/pages/login/Register.tsx deleted file mode 100644 index 019cd0d2e3b..00000000000 --- a/client/dashboard/src/pages/login/Register.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { useSession } from "@/contexts/Auth"; -import { safeSameOriginPath, safeSameOriginUrl } from "@/lib/safe-external-url"; -import { Navigate, useSearchParams } from "react-router"; - -export default function Register(): JSX.Element { - const session = useSession(); - const [searchParams] = useSearchParams(); - - const disposition = searchParams.get("disposition"); - if (disposition === "assistants") { - return ( - - ); - } - - const rawRedirect = searchParams.get("redirect"); - const redirect = safeSameOriginUrl(rawRedirect); - const signUpTarget = redirect - ? "/sign-up?redirect=" + encodeURIComponent(redirect) - : "/sign-up"; - - if (session.session === "" || session.activeOrganizationId === "") { - return ; - } - - return ; -} diff --git a/client/dashboard/src/pages/login/components/terms-footer.tsx b/client/dashboard/src/pages/login/components/terms-footer.tsx index b167602edad..e15fc620e09 100644 --- a/client/dashboard/src/pages/login/components/terms-footer.tsx +++ b/client/dashboard/src/pages/login/components/terms-footer.tsx @@ -1,6 +1,6 @@ import { cn } from "@/lib/utils"; -// Legal footer shared by every auth surface (login/register shell and the +// Legal footer shared by every auth surface (login/signup shell and the // demo-gate AuthLayout) so the links and copy stay in one place. export function TermsFooter({ className, diff --git a/client/dashboard/src/routes.tsx b/client/dashboard/src/routes.tsx index 5b1807fe5e4..83f3e8ae898 100644 --- a/client/dashboard/src/routes.tsx +++ b/client/dashboard/src/routes.tsx @@ -1,7 +1,7 @@ import { Icon, IconProps } from "@/components/ui/Icon"; import { IconName } from "@/components/ui/Icon/names"; import React, { useMemo } from "react"; -import { Link, useLocation, useNavigate } from "react-router"; +import { Link, Navigate, useLocation, useNavigate } from "react-router"; import { ReleaseStage } from "./components/release-stage-badge"; import { useSlugs } from "./contexts/Sdk"; import { cn } from "./lib/utils"; @@ -42,7 +42,6 @@ import Home from "./pages/home/Home"; import { ProjectGuidePage } from "./components/project-guide/ProjectGuidePage"; import Integrations from "./pages/integrations/Integrations"; import Login from "./pages/login/Login"; -import Register from "./pages/login/Register"; import ExploreDemo from "./pages/demo/ExploreDemo"; import SignUp from "./pages/login/SignUp"; import { LogsRoot } from "./pages/logs/Logs"; @@ -236,7 +235,7 @@ const ROUTE_STRUCTURE = { register: { title: "Register", url: "/register", - component: Register, + component: () => , unauthenticated: true, }, exploreDemo: {