From 04fd454af6ec16536e77e6bc0b1868563fad9cf5 Mon Sep 17 00:00:00 2001 From: Dotta Date: Wed, 3 Jun 2026 19:29:07 +0000 Subject: [PATCH] PAP-10257 Fix Team Catalog routes dropping /PAP/ company prefix on navigate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add "teams" to BOARD_ROUTE_ROOTS so extractCompanyPrefixFromPath no longer misreads the first segment of `/teams/` as a company prefix ("TEAMS"). Without this, useNavigate skipped the re-prefix rewrite and auto-select / row clicks / file-tree clicks landed on `/teams/` → "Company not found". Mirrors the sibling `skills` route precedent. Bonus: rename the search filter URL param from `q` to `search` so it matches the sibling full-word params (kind/category/risk). `?search=` now both filters the list and populates the search input, fixing the empty-textbox finding. Adds a regression unit test covering prefix-preserving navigation for team routes (auto-select, nested /files paths, idempotency, round-trip). Co-Authored-By: Claude Opus 4.8 --- ui/src/lib/company-routes.test.ts | 22 ++++++++++++++++++++++ ui/src/lib/company-routes.ts | 1 + ui/src/pages/TeamCatalog.tsx | 4 ++-- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/ui/src/lib/company-routes.test.ts b/ui/src/lib/company-routes.test.ts index 0c2ed6a01e1..feb30e6ca5b 100644 --- a/ui/src/lib/company-routes.test.ts +++ b/ui/src/lib/company-routes.test.ts @@ -35,4 +35,26 @@ describe("company routes", () => { expect(applyCompanyPrefix("/search?q=hello%20world", "PAP")).toBe("/PAP/search?q=hello%20world"); expect(toCompanyRelativePath("/PAP/search?q=foo")).toBe("/search?q=foo"); }); + + // Regression for PAP-10257: Team Catalog navigation (auto-select + row/file + // clicks) produces company-relative `/teams/` paths. Without `teams` in + // the board-route allowlist, `extractCompanyPrefixFromPath` misread the first + // segment as a company prefix ("TEAMS") and `useNavigate` skipped the rewrite, + // dropping the `/PAP/` prefix and crashing into "Company not found". + it("re-prefixes team catalog routes so navigate preserves the company prefix", () => { + expect(isBoardPathWithoutPrefix("/teams")).toBe(true); + expect(isBoardPathWithoutPrefix("/teams/core-exec-team")).toBe(true); + expect(extractCompanyPrefixFromPath("/teams/core-exec-team")).toBeNull(); + + // Auto-select effect: `/teams/` must gain the `/PAP/` prefix. + expect(applyCompanyPrefix("/teams/core-exec-team", "PAP")).toBe("/PAP/teams/core-exec-team"); + // File-tree click: nested `/files/` path is preserved under the prefix. + expect(applyCompanyPrefix("/teams/core-exec-team/files/TEAM.md", "PAP")).toBe( + "/PAP/teams/core-exec-team/files/TEAM.md", + ); + // Already-prefixed paths are left untouched (idempotent — no double prefix). + expect(applyCompanyPrefix("/PAP/teams/core-exec-team", "PAP")).toBe("/PAP/teams/core-exec-team"); + // Round-trips back to a company-relative path. + expect(toCompanyRelativePath("/PAP/teams/core-exec-team")).toBe("/teams/core-exec-team"); + }); }); diff --git a/ui/src/lib/company-routes.ts b/ui/src/lib/company-routes.ts index bde7b10fb9c..5410252383f 100644 --- a/ui/src/lib/company-routes.ts +++ b/ui/src/lib/company-routes.ts @@ -3,6 +3,7 @@ const BOARD_ROUTE_ROOTS = new Set([ "companies", "company", "skills", + "teams", "org", "agents", "projects", diff --git a/ui/src/pages/TeamCatalog.tsx b/ui/src/pages/TeamCatalog.tsx index a46cee584b1..555362883ea 100644 --- a/ui/src/pages/TeamCatalog.tsx +++ b/ui/src/pages/TeamCatalog.tsx @@ -2129,7 +2129,7 @@ export function TeamCatalog() { const selectedRef = parsedRoute.catalogRef; const selectedFilePath = parsedRoute.filePath; - const q = searchParams.get("q") ?? ""; + const q = searchParams.get("search") ?? ""; const kindFilter = (searchParams.get("kind") as KindFilter) ?? "all"; const categoryFilter = searchParams.get("category") ?? ""; const riskFilter = (searchParams.get("risk") as RiskFilter) ?? "any"; @@ -2229,7 +2229,7 @@ export function TeamCatalog() { setFilterParam("q", e.target.value)} + onChange={(e) => setFilterParam("search", e.target.value)} placeholder="Search teams" className="h-8 w-56 pl-8" />