From 33a28686faf30d8e4d15c983e282b3f9f1230285 Mon Sep 17 00:00:00 2001 From: Quick104 <31828688+Quick104@users.noreply.github.com> Date: Thu, 8 Oct 2026 12:11:50 -0400 Subject: [PATCH] fix(web): rename admin Home rows back to Sections Admin > Home rows returns to its earlier name, Sections, at /admin/sections. /admin/home-rows, used since #1877, now redirects there and keeps ?page= and the hash. Copy that named the page follows; "row" stays the word for one row on it. Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/architecture/collection-editor.md | 22 ++++++------ web/src/App.adminRoutes.test.tsx | 18 +++++----- web/src/App.tsx | 6 ++-- web/src/components/AdminLayout.test.tsx | 10 +++--- web/src/components/AdminSidebar.test.tsx | 8 ++--- .../collections/StarterPacksDialog.test.tsx | 4 +-- .../collections/StarterPacksDialog.tsx | 2 +- .../components/homeRows/HomeRowsPage.test.tsx | 2 +- .../homeRows/RestoreDialog.test.tsx | 2 +- web/src/components/homeRows/RestoreDialog.tsx | 2 +- web/src/components/homeRows/notes.tsx | 2 +- .../homeRows/useAdminHomeRows.test.tsx | 22 ++++++------ web/src/lib/adminNavigation.test.ts | 4 +-- web/src/lib/adminNavigation.ts | 6 ++-- web/src/lib/collections/copy.test.ts | 10 +++--- web/src/lib/collections/copy.ts | 4 +-- web/src/lib/collections/rows.test.ts | 2 +- web/src/lib/documentTitle.test.ts | 2 +- web/src/lib/documentTitle.ts | 2 +- web/src/lib/homeRows/rowLinks.test.ts | 2 +- web/src/lib/homeRows/rowLinks.ts | 2 +- web/src/pages/AdminCollections.list.test.tsx | 10 +++--- web/src/pages/AdminCollections.tsx | 2 +- web/src/pages/AdminHomeRows.addRow.test.tsx | 10 +++--- .../pages/AdminHomeRows.concurrency.test.tsx | 10 +++--- .../AdminHomeRows.libraryCopies.test.tsx | 8 ++--- web/src/pages/AdminHomeRows.links.test.tsx | 34 +++++++++---------- web/src/pages/AdminHomeRows.peeks.test.tsx | 8 ++--- web/src/pages/AdminHomeRows.tsx | 4 +-- ...CollectionEditorPage.whereItShows.test.tsx | 20 +++++------ web/src/pages/Collections.cards.test.tsx | 2 +- ...x => LegacyAdminHomeRowsRedirect.test.tsx} | 24 ++++++------- web/src/pages/LegacyAdminHomeRowsRedirect.tsx | 10 ++++++ web/src/pages/LegacyAdminSectionsRedirect.tsx | 10 ------ web/src/pages/LibraryRecommended.test.tsx | 2 +- web/src/pages/LibraryRecommended.tsx | 4 +-- 36 files changed, 144 insertions(+), 148 deletions(-) rename web/src/pages/{LegacyAdminSectionsRedirect.test.tsx => LegacyAdminHomeRowsRedirect.test.tsx} (55%) create mode 100644 web/src/pages/LegacyAdminHomeRowsRedirect.tsx delete mode 100644 web/src/pages/LegacyAdminSectionsRedirect.tsx diff --git a/docs/architecture/collection-editor.md b/docs/architecture/collection-editor.md index a3c9fe20a8..e1174576ce 100644 --- a/docs/architecture/collection-editor.md +++ b/docs/architecture/collection-editor.md @@ -93,7 +93,7 @@ change forward or stops at the conflict. ## Smart rules and the live preview -A Smart collection's Contents is the Home rows rule sentence (`RuleBuilder`), with the libraries +A Smart collection's Contents is Add row's rule sentence (`RuleBuilder`), with the libraries inside it: a server collection needs at least one, and a personal one with none matches every library the profile can see. Rules about the viewer (Watched and the like) are offered only on a personal collection. A rule the builder can't show stays as a locked line and is saved unchanged @@ -190,9 +190,9 @@ personal collection searches every library the profile can see. The search is a not the access check: the personal item route refuses a title the acting profile can't see, and a server collection's members are filtered per viewer when they are read. -## Links into Home rows +## Links into the row pages -Both Home rows pages (`/admin/home-rows` and `/settings/home-screen`) take link parameters next to +Both row pages (admin Sections at `/admin/sections`, and `/settings/home-screen`) take link parameters next to `?page=`, read by `web/src/lib/homeRows/rowLinks.ts` and followed by `web/src/components/homeRows/useRowLinks.ts`. Each page reads them once and drops them from the address, so a reload doesn't repeat them. @@ -210,9 +210,9 @@ address, so a reload doesn't repeat them. - `?edit=` opens that row in Edit row; a row that is gone gets a toast. - `?return=` makes the dialog's back link "Back to *collection*" and, after Add row, goes to that path with a toast that offers to move the new row (`?edit=` on the same page). Both replace - the Home rows history entry. Settings > Home Screen queues its saves, so there it goes back only + the row page's history entry. Settings > Home Screen queues its saves, so there it goes back only once the save lands and the row is on the page, even when that takes a later read because the - one after the save failed; a failed save keeps the viewer on Home rows with the save's error + one after the save failed; a failed save keeps the viewer on Home Screen with the save's error toast. The toast's position counts the page's rows when the add lands, not when the dialog opened. A link that arrives while the dialog is open starts a fresh dialog on its target. After Add row the navigation state carries `addedRow` (the `AddedRowState` type: new row id, surface, page and position) so @@ -230,19 +230,19 @@ its "On Home and the _Kids_ page" (turned-off rows left out). Rows profiles add are not listed, because the route reads the administrator page layouts only. When the collections capabilities don't report `section_references`, the list is left out and Add as a row stays. -- **Add as a row** goes to admin Home rows with `?page=…&add=collection:library:` and, from the - editor, `?return=` set to the editor's own URL (see Links into Home rows). The List's Add to Home +- **Add as a row** goes to admin Sections with `?page=…&add=collection:library:` and, from the + editor, `?return=` set to the editor's own URL (see Links into the row pages). The List's Add to Home and Add to a library page send no `return`, because the List's address is not a collection page - the allowlist accepts; Home rows keeps its own after-add step. With unsaved changes the editor + the allowlist accepts; Sections keeps its own after-add step. With unsaved changes the editor asks first: Save and continue saves and goes only once nothing is left unsaved (artwork that failed to upload stays in the editor with its error), Discard changes drops the draft (titles stay), Cancel stays. Discard changes is off when the collection is saved as hidden, since only saving would show it. Save and continue is off whenever Save is, including while a field changed in both places waits for Keep mine or Use theirs. A collection not created yet, or one hidden from its Collections tab, can't be added, because a row couldn't open its See all; the button says why. -- **Back from Home rows**, the `addedRow` state names the new row, which is highlighted once the - rows list includes it. The editor sends Home rows the List view it was opened from as history - state, and Home rows hands that state back with `addedRow`, so the editor's Back still reaches +- **Back from Sections**, the `addedRow` state names the new row, which is highlighted once the + rows list includes it. The editor sends Sections the List view it was opened from as history + state, and Sections hands that state back with `addedRow`, so the editor's Back still reaches that view. - **Hiding** a collection rows show asks first, naming the pages the rows are on, as the List does with its `row_count`. While the rows list hasn't loaded (or failed), the List's `row_count` for diff --git a/web/src/App.adminRoutes.test.tsx b/web/src/App.adminRoutes.test.tsx index 0e16c5750a..a316fa52d1 100644 --- a/web/src/App.adminRoutes.test.tsx +++ b/web/src/App.adminRoutes.test.tsx @@ -42,7 +42,7 @@ vi.mock("@/hooks/useAuth", async () => { vi.mock("@/hooks/useIsActingAdmin", () => ({ useIsActingAdmin: () => true })); vi.mock("@/components/AdminLayout", () => ({ default: () => })); -vi.mock("@/pages/AdminHomeRows", () => ({ default: () =>
Home rows page
})); +vi.mock("@/pages/AdminHomeRows", () => ({ default: () =>
Sections page
})); import App from "@/App"; @@ -51,23 +51,23 @@ describe("App admin Home rows routes", () => { appRouter = null; }); - it("serves the Home rows page at /admin/home-rows", async () => { - initialEntry = "/admin/home-rows"; + it("serves the Sections page at /admin/sections", async () => { + initialEntry = "/admin/sections"; render(); - expect(await screen.findByText("Home rows page")).toBeInTheDocument(); - expect(appRouter?.state.location.pathname).toBe("/admin/home-rows"); + expect(await screen.findByText("Sections page")).toBeInTheDocument(); + expect(appRouter?.state.location.pathname).toBe("/admin/sections"); }); - it("sends the old /admin/sections links to Home rows, keeping the query", async () => { - initialEntry = "/admin/sections?page=page-2#rows"; + it("sends /admin/home-rows links to Sections, keeping the query", async () => { + initialEntry = "/admin/home-rows?page=page-2#rows"; render(); - expect(await screen.findByText("Home rows page")).toBeInTheDocument(); + expect(await screen.findByText("Sections page")).toBeInTheDocument(); expect(appRouter?.state.location).toMatchObject({ - pathname: "/admin/home-rows", + pathname: "/admin/sections", search: "?page=page-2", hash: "#rows", }); diff --git a/web/src/App.tsx b/web/src/App.tsx index 68377eeb52..be6b6e2eab 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -61,7 +61,7 @@ import { buildUserCollectionCatalogHref, } from "@/pages/catalogSearchParams"; import { buildLegacyAutoscanRedirectTarget } from "@/pages/autoscanSearchParams"; -import LegacyAdminSectionsRedirect from "@/pages/LegacyAdminSectionsRedirect"; +import LegacyAdminHomeRowsRedirect from "@/pages/LegacyAdminHomeRowsRedirect"; import LegacyRequestDetailRedirect from "@/pages/LegacyRequestDetailRedirect"; import { buildLegacyWebhookSyncRedirectTarget } from "@/lib/webhookSync"; import { guardRedirectTarget } from "@/lib/authRedirect"; @@ -540,8 +540,8 @@ function AppRoutes() { } /> } /> } /> - } /> - } /> + } /> + } /> } /> } /> } /> diff --git a/web/src/components/AdminLayout.test.tsx b/web/src/components/AdminLayout.test.tsx index da3578763d..c8792d37e5 100644 --- a/web/src/components/AdminLayout.test.tsx +++ b/web/src/components/AdminLayout.test.tsx @@ -49,7 +49,7 @@ function renderAdmin(initialPath = "/admin") { children: [ { index: true, element:

Admin dashboard

}, { path: "users", element:

Admin users

}, - { path: "home-rows", element:

Admin home rows

}, + { path: "sections", element:

Admin sections

}, ], }, ], @@ -122,11 +122,11 @@ describe("AdminLayout page title", () => { // The tab title, the route-change announcement and the phone header all read // this name, so a renamed route must not fall back to the generic "Admin" // (which the phone header shows as "Dashboard"). - it("names the Home rows page in the tab and the phone header", () => { - renderAdmin("/admin/home-rows"); + it("names the Sections page in the tab and the phone header", () => { + renderAdmin("/admin/sections"); - expect(document.title).toBe("Admin Home rows · Silo"); - expect(screen.getByText("Home rows")).toBeInTheDocument(); + expect(document.title).toBe("Admin Sections · Silo"); + expect(screen.getByText("Sections")).toBeInTheDocument(); expect(screen.queryByText("Dashboard")).not.toBeInTheDocument(); }); }); diff --git a/web/src/components/AdminSidebar.test.tsx b/web/src/components/AdminSidebar.test.tsx index 639b66563f..35d0abcd7a 100644 --- a/web/src/components/AdminSidebar.test.tsx +++ b/web/src/components/AdminSidebar.test.tsx @@ -111,12 +111,12 @@ describe("AdminSidebar", () => { expect(markup).not.toContain("/admin/settings?tab="); }); - it("includes a Home rows link in the content navigation", () => { + it("includes a Sections link in the content navigation", () => { const markup = renderSidebar(); - expect(markup).toContain('href="/admin/home-rows"'); - expect(markup).toContain(">Home rows<"); - expect(markup).not.toContain('href="/admin/sections"'); + expect(markup).toContain('href="/admin/sections"'); + expect(markup).toContain(">Sections<"); + expect(markup).not.toContain('href="/admin/home-rows"'); }); it("hides Policy navigation when the editor capability is unavailable", () => { diff --git a/web/src/components/collections/StarterPacksDialog.test.tsx b/web/src/components/collections/StarterPacksDialog.test.tsx index 6bcdb5c80c..fb3ac92c30 100644 --- a/web/src/components/collections/StarterPacksDialog.test.tsx +++ b/web/src/components/collections/StarterPacksDialog.test.tsx @@ -189,11 +189,11 @@ describe("Starter packs", () => { expect(screen.getByRole("button", { name: "Add 4 collections" })).toBeEnabled(); }); - it("says where new lists land and that no Home rows are added", async () => { + it("says where new lists land and that no sections are added", async () => { renderDialog(); await checked(); expect(screen.getByText(/New lists land under/)).toHaveTextContent( - "New lists land under No heading on each library's Collections tab, ready to move into a shelf in Arrange. “Already there” lists aren't touched. No Home rows are added unless you turn on hero banners.", + "New lists land under No heading on each library's Collections tab, ready to move into a shelf in Arrange. “Already there” lists aren't touched. No sections are added unless you turn on hero banners.", ); }); diff --git a/web/src/components/collections/StarterPacksDialog.tsx b/web/src/components/collections/StarterPacksDialog.tsx index cc02b14dbd..839409828f 100644 --- a/web/src/components/collections/StarterPacksDialog.tsx +++ b/web/src/components/collections/StarterPacksDialog.tsx @@ -708,7 +708,7 @@ function WhatWillHappen({ aren't touched.{" "} {heroesOn ? "Hero banners are added to the pages you picked below." - : "No Home rows are added unless you turn on hero banners."} + : "No sections are added unless you turn on hero banners."}

diff --git a/web/src/components/homeRows/HomeRowsPage.test.tsx b/web/src/components/homeRows/HomeRowsPage.test.tsx index 506df53b1b..718828bb72 100644 --- a/web/src/components/homeRows/HomeRowsPage.test.tsx +++ b/web/src/components/homeRows/HomeRowsPage.test.tsx @@ -264,7 +264,7 @@ describe("HomeRowsPage", () => { it("offers Reload rows when the rows changed elsewhere", async () => { render(); const banner = screen.getByRole("alert"); - expect(banner).toHaveTextContent("Home rows changed since you opened this page."); + expect(banner).toHaveTextContent("These rows changed since you opened this page."); await userEvent.click(within(banner).getByRole("button", { name: "Reload rows" })); expect(harness.reload).toHaveBeenCalled(); }); diff --git a/web/src/components/homeRows/RestoreDialog.test.tsx b/web/src/components/homeRows/RestoreDialog.test.tsx index 5d6964e9d0..1a0bf23dd5 100644 --- a/web/src/components/homeRows/RestoreDialog.test.tsx +++ b/web/src/components/homeRows/RestoreDialog.test.tsx @@ -122,7 +122,7 @@ describe("RestoreDialog", () => { rerender(); expect(screen.getByRole("alert")).toHaveTextContent( - "Home rows changed since you opened this. Reload to see the current rows, then try again.", + "These rows changed since you opened this. Reload to see the current rows, then try again.", ); expect(screen.queryByRole("button", { name: "Restore defaults" })).not.toBeInTheDocument(); await userEvent.click(screen.getByRole("button", { name: "Reload rows" })); diff --git a/web/src/components/homeRows/RestoreDialog.tsx b/web/src/components/homeRows/RestoreDialog.tsx index 47304e0582..f747e84158 100644 --- a/web/src/components/homeRows/RestoreDialog.tsx +++ b/web/src/components/homeRows/RestoreDialog.tsx @@ -159,7 +159,7 @@ export function RestoreDialog({ >

- Home rows changed since you opened this. Reload to see the current rows, then try + These rows changed since you opened this. Reload to see the current rows, then try again.