From a34976c409f284b35be784d00f55b6ab6b934e4f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 12:47:31 +0000 Subject: [PATCH 1/2] fix: rename the Projects org settings URL to /settings/projects The org settings page at /orgs/:organizationSlug/settings/runtime-updates was rebuilt into a general "Projects" page, but its URL still described only the runtime-update banner at the top of it. Move the page to /orgs/:organizationSlug/settings/projects and rename the path helper and page component to match. The old URL keeps working: a redirect-only loader at the old segment sends requests to the new path, preserving the org slug and any query string. The `/_/` deeplink table gains a `projects` key and keeps `runtime-updates` as an alias for the same page, so links already shared under either name resolve. Names that describe the runtime-update feature rather than the URL (projectRuntimeUpdates.server, organizationHasProjectRuntimeUpdate, ProjectRuntimeRow) are left alone, since they are still accurate. Co-Authored-By: Claude --- .../navigation/OrganizationSettingsSideMenu.tsx | 6 +++--- apps/webapp/app/routes/[_].$.ts | 10 +++++----- .../ProjectsPage.tsx} | 2 +- .../route.tsx | 4 ++-- ....$organizationSlug.settings.runtime-updates.ts | 10 ++++++++++ apps/webapp/app/utils/deeplinkPages.test.ts | 15 ++++++++++++--- apps/webapp/app/utils/deeplinkPages.ts | 5 ++++- apps/webapp/app/utils/pathBuilder.ts | 4 ++-- 8 files changed, 39 insertions(+), 17 deletions(-) rename apps/webapp/app/routes/{_app.orgs.$organizationSlug.settings.runtime-updates/RuntimeUpdatesPage.tsx => _app.orgs.$organizationSlug.settings.projects/ProjectsPage.tsx} (99%) rename apps/webapp/app/routes/{_app.orgs.$organizationSlug.settings.runtime-updates => _app.orgs.$organizationSlug.settings.projects}/route.tsx (96%) create mode 100644 apps/webapp/app/routes/orgs.$organizationSlug.settings.runtime-updates.ts diff --git a/apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx b/apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx index 56b988e3e7f..3087896019c 100644 --- a/apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx +++ b/apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx @@ -16,8 +16,8 @@ import { type MatchedOrganization } from "~/hooks/useOrganizations"; import { cn } from "~/utils/cn"; import { organizationPath, + organizationProjectsPath, organizationRolesPath, - organizationRuntimeUpdatesPath, organizationSettingsPath, organizationSlackIntegrationPath, organizationSsoPath, @@ -136,8 +136,8 @@ export function OrganizationSettingsSideMenu({ icon={FolderOpenIcon} activeIconColor="text-text-bright" inactiveIconColor="text-text-dimmed" - to={organizationRuntimeUpdatesPath(organization)} - data-action="runtime-updates" + to={organizationProjectsPath(organization)} + data-action="projects" badge={ hasProjectRuntimeUpdate ? ( <> diff --git a/apps/webapp/app/routes/[_].$.ts b/apps/webapp/app/routes/[_].$.ts index 25a4e1835ef..f8b92b23c61 100644 --- a/apps/webapp/app/routes/[_].$.ts +++ b/apps/webapp/app/routes/[_].$.ts @@ -12,7 +12,7 @@ import { invitesPath, newOrganizationPath, newProjectPath, - organizationRuntimeUpdatesPath, + organizationProjectsPath, v3EnvironmentPath, } from "~/utils/pathBuilder"; @@ -33,8 +33,8 @@ export const loader = async ({ request }: LoaderFunctionArgs) => { const presenter = new SelectBestEnvironmentPresenter(); try { const { project, organization, environment } = await presenter.call({ user }); - if (organizationPage === "runtime-updates") { - return redirect(`${organizationRuntimeUpdatesPath(organization)}${search}`); + if (organizationPage === "projects") { + return redirect(`${organizationProjectsPath(organization)}${search}`); } const environmentPath = v3EnvironmentPath(organization, project, environment); @@ -57,8 +57,8 @@ export const loader = async ({ request }: LoaderFunctionArgs) => { }); if (organization) { - if (organizationPage === "runtime-updates") { - return redirect(`${organizationRuntimeUpdatesPath(organization)}${search}`); + if (organizationPage === "projects") { + return redirect(`${organizationProjectsPath(organization)}${search}`); } return redirect(newProjectPath(organization)); diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.runtime-updates/RuntimeUpdatesPage.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.projects/ProjectsPage.tsx similarity index 99% rename from apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.runtime-updates/RuntimeUpdatesPage.tsx rename to apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.projects/ProjectsPage.tsx index efa4c1dfe66..71655b1f1fb 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.runtime-updates/RuntimeUpdatesPage.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.projects/ProjectsPage.tsx @@ -42,7 +42,7 @@ export type ProjectRuntimeRow = { } | null; }; -export function RuntimeUpdatesPage({ +export function ProjectsPage({ organizationSlug, needsUpdate, otherProjects, diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.runtime-updates/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.projects/route.tsx similarity index 96% rename from apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.runtime-updates/route.tsx rename to apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.projects/route.tsx index 37bfba0fec3..23a46c65a84 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.runtime-updates/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.projects/route.tsx @@ -6,7 +6,7 @@ import { dashboardLoader } from "~/services/routeBuilders/dashboardBuilder"; import { getUserId } from "~/services/session.server"; import { pageMeta } from "~/utils/pageTitle"; import { OrganizationParamsSchema } from "~/utils/pathBuilder"; -import { type ProjectRuntimeRow, RuntimeUpdatesPage } from "./RuntimeUpdatesPage"; +import { type ProjectRuntimeRow, ProjectsPage } from "./ProjectsPage"; export const meta = pageMeta("Projects"); @@ -73,7 +73,7 @@ export default function Page() { const { organizationSlug, needsUpdate, otherProjects } = useTypedLoaderData(); return ( - { + const { organizationSlug } = OrganizationParamsSchema.parse(params); + const { search } = new URL(request.url); + return redirect(`${organizationProjectsPath({ slug: organizationSlug })}${search}`); +}; diff --git a/apps/webapp/app/utils/deeplinkPages.test.ts b/apps/webapp/app/utils/deeplinkPages.test.ts index 5c3800c91c5..3c6110702a0 100644 --- a/apps/webapp/app/utils/deeplinkPages.test.ts +++ b/apps/webapp/app/utils/deeplinkPages.test.ts @@ -164,11 +164,20 @@ describe("resolveDeeplinkPage", () => { }); it("resolves organization-level pages separately from environment pages", () => { + expect(ORG_PAGE_TARGETS.get("projects")).toEqual({ + landing: "projects", + prefix: "projects", + }); + expect(resolveOrganizationDeeplinkPage("projects")).toBe("projects"); + expect(resolveDeeplinkPage("projects")).toBeUndefined(); + }); + + it("keeps the legacy runtime-updates deeplink pointing at the projects page", () => { expect(ORG_PAGE_TARGETS.get("runtime-updates")).toEqual({ - landing: "runtime-updates", - prefix: "runtime-updates", + landing: "projects", + prefix: "projects", }); - expect(resolveOrganizationDeeplinkPage("runtime-updates")).toBe("runtime-updates"); + expect(resolveOrganizationDeeplinkPage("runtime-updates")).toBe("projects"); expect(resolveDeeplinkPage("runtime-updates")).toBeUndefined(); }); diff --git a/apps/webapp/app/utils/deeplinkPages.ts b/apps/webapp/app/utils/deeplinkPages.ts index 9a03ef386e8..831b1173b55 100644 --- a/apps/webapp/app/utils/deeplinkPages.ts +++ b/apps/webapp/app/utils/deeplinkPages.ts @@ -39,7 +39,10 @@ export const ENV_PAGE_TARGETS: ReadonlyMap = new Map([ ]); export const ORG_PAGE_TARGETS: ReadonlyMap = new Map([ - ["runtime-updates", page("runtime-updates")], + ["projects", page("projects")], + // Alias: `/_/runtime-updates` links are already out in the wild, so keep them landing on the + // same page as `/_/projects`. + ["runtime-updates", page("projects")], ]); export const DEEPLINK_PATH_PREFIX = "/_"; diff --git a/apps/webapp/app/utils/pathBuilder.ts b/apps/webapp/app/utils/pathBuilder.ts index 1db150a368b..95727d6c76a 100644 --- a/apps/webapp/app/utils/pathBuilder.ts +++ b/apps/webapp/app/utils/pathBuilder.ts @@ -170,8 +170,8 @@ export function organizationSettingsPath(organization: OrgForPath) { return `${organizationPath(organization)}/settings`; } -export function organizationRuntimeUpdatesPath(organization: OrgForPath) { - return `${organizationSettingsPath(organization)}/runtime-updates`; +export function organizationProjectsPath(organization: OrgForPath) { + return `${organizationSettingsPath(organization)}/projects`; } function organizationIntegrationsPath(organization: OrgForPath) { From 3cf89da0d7ba780f1dc6c011278c6b378bb3e98c Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 13:01:35 +0000 Subject: [PATCH 2/2] fix: remove the /_/runtime-updates deeplink alias Co-Authored-By: Claude --- apps/webapp/app/utils/deeplinkPages.test.ts | 9 +++------ apps/webapp/app/utils/deeplinkPages.ts | 3 --- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/apps/webapp/app/utils/deeplinkPages.test.ts b/apps/webapp/app/utils/deeplinkPages.test.ts index 3c6110702a0..597135cb311 100644 --- a/apps/webapp/app/utils/deeplinkPages.test.ts +++ b/apps/webapp/app/utils/deeplinkPages.test.ts @@ -172,12 +172,9 @@ describe("resolveDeeplinkPage", () => { expect(resolveDeeplinkPage("projects")).toBeUndefined(); }); - it("keeps the legacy runtime-updates deeplink pointing at the projects page", () => { - expect(ORG_PAGE_TARGETS.get("runtime-updates")).toEqual({ - landing: "projects", - prefix: "projects", - }); - expect(resolveOrganizationDeeplinkPage("runtime-updates")).toBe("projects"); + it("does not carry a runtime-updates alias", () => { + expect(ORG_PAGE_TARGETS.has("runtime-updates")).toBe(false); + expect(resolveOrganizationDeeplinkPage("runtime-updates")).toBeUndefined(); expect(resolveDeeplinkPage("runtime-updates")).toBeUndefined(); }); diff --git a/apps/webapp/app/utils/deeplinkPages.ts b/apps/webapp/app/utils/deeplinkPages.ts index 831b1173b55..1ba60b354f9 100644 --- a/apps/webapp/app/utils/deeplinkPages.ts +++ b/apps/webapp/app/utils/deeplinkPages.ts @@ -40,9 +40,6 @@ export const ENV_PAGE_TARGETS: ReadonlyMap = new Map([ export const ORG_PAGE_TARGETS: ReadonlyMap = new Map([ ["projects", page("projects")], - // Alias: `/_/runtime-updates` links are already out in the wild, so keep them landing on the - // same page as `/_/projects`. - ["runtime-updates", page("projects")], ]); export const DEEPLINK_PATH_PREFIX = "/_";