From cf77ff927f032c72b1ed0d3dad018b63916f7b7a Mon Sep 17 00:00:00 2001 From: Dotta Date: Thu, 12 Mar 2026 16:04:28 -0500 Subject: [PATCH] Fix manual company switch route sync Co-Authored-By: Paperclip --- ui/src/components/Layout.tsx | 11 ++++++++- ui/src/context/CompanyContext.tsx | 3 +-- ui/src/lib/company-selection.test.ts | 34 ++++++++++++++++++++++++++++ ui/src/lib/company-selection.ts | 18 +++++++++++++++ 4 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 ui/src/lib/company-selection.test.ts create mode 100644 ui/src/lib/company-selection.ts diff --git a/ui/src/components/Layout.tsx b/ui/src/components/Layout.tsx index a90efa9a..e484b265 100644 --- a/ui/src/components/Layout.tsx +++ b/ui/src/components/Layout.tsx @@ -22,6 +22,7 @@ import { useTheme } from "../context/ThemeContext"; import { useKeyboardShortcuts } from "../hooks/useKeyboardShortcuts"; import { useCompanyPageMemory } from "../hooks/useCompanyPageMemory"; import { healthApi } from "../api/health"; +import { shouldSyncCompanySelectionFromRoute } from "../lib/company-selection"; import { queryKeys } from "../lib/queryKeys"; import { cn } from "../lib/utils"; import { NotFoundPage } from "../pages/NotFound"; @@ -36,6 +37,7 @@ export function Layout() { loading: companiesLoading, selectedCompany, selectedCompanyId, + selectionSource, setSelectedCompanyId, } = useCompany(); const { theme, toggleTheme } = useTheme(); @@ -88,7 +90,13 @@ export function Layout() { return; } - if (selectedCompanyId !== matchedCompany.id) { + if ( + shouldSyncCompanySelectionFromRoute({ + selectionSource, + selectedCompanyId, + routeCompanyId: matchedCompany.id, + }) + ) { setSelectedCompanyId(matchedCompany.id, { source: "route_sync" }); } }, [ @@ -99,6 +107,7 @@ export function Layout() { location.pathname, location.search, navigate, + selectionSource, selectedCompanyId, setSelectedCompanyId, ]); diff --git a/ui/src/context/CompanyContext.tsx b/ui/src/context/CompanyContext.tsx index eafc7f55..fb074f33 100644 --- a/ui/src/context/CompanyContext.tsx +++ b/ui/src/context/CompanyContext.tsx @@ -12,8 +12,7 @@ import type { Company } from "@paperclipai/shared"; import { companiesApi } from "../api/companies"; import { ApiError } from "../api/client"; import { queryKeys } from "../lib/queryKeys"; - -type CompanySelectionSource = "manual" | "route_sync" | "bootstrap"; +import type { CompanySelectionSource } from "../lib/company-selection"; type CompanySelectionOptions = { source?: CompanySelectionSource }; interface CompanyContextValue { diff --git a/ui/src/lib/company-selection.test.ts b/ui/src/lib/company-selection.test.ts new file mode 100644 index 00000000..a8533a4b --- /dev/null +++ b/ui/src/lib/company-selection.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from "vitest"; +import { shouldSyncCompanySelectionFromRoute } from "./company-selection"; + +describe("shouldSyncCompanySelectionFromRoute", () => { + it("does not resync when selection already matches the route", () => { + expect( + shouldSyncCompanySelectionFromRoute({ + selectionSource: "route_sync", + selectedCompanyId: "pap", + routeCompanyId: "pap", + }), + ).toBe(false); + }); + + it("defers route sync while a manual company switch is in flight", () => { + expect( + shouldSyncCompanySelectionFromRoute({ + selectionSource: "manual", + selectedCompanyId: "pap", + routeCompanyId: "ret", + }), + ).toBe(false); + }); + + it("syncs back to the route company for non-manual mismatches", () => { + expect( + shouldSyncCompanySelectionFromRoute({ + selectionSource: "route_sync", + selectedCompanyId: "pap", + routeCompanyId: "ret", + }), + ).toBe(true); + }); +}); diff --git a/ui/src/lib/company-selection.ts b/ui/src/lib/company-selection.ts new file mode 100644 index 00000000..ce02cb4d --- /dev/null +++ b/ui/src/lib/company-selection.ts @@ -0,0 +1,18 @@ +export type CompanySelectionSource = "manual" | "route_sync" | "bootstrap"; + +export function shouldSyncCompanySelectionFromRoute(params: { + selectionSource: CompanySelectionSource; + selectedCompanyId: string | null; + routeCompanyId: string; +}): boolean { + const { selectionSource, selectedCompanyId, routeCompanyId } = params; + + if (selectedCompanyId === routeCompanyId) return false; + + // Let manual company switches finish their remembered-path navigation first. + if (selectionSource === "manual" && selectedCompanyId) { + return false; + } + + return true; +}