From 564bfc47790162ca774b3965653d694a425f20bb Mon Sep 17 00:00:00 2001 From: Samuel Ajayi Date: Sun, 26 Jul 2026 09:37:52 +0100 Subject: [PATCH] fix: gate dev-error routes centrally and exclude from breadcrumbs - Add dev-errors/layout.tsx as single NODE_ENV gate (replaces 3 per-page notFound() guards) - Filter devOnly routes from buildBreadcrumbsFromPath() so /dashboard/dev-errors/... no longer shows in breadcrumb navigation - Add breadcrumb test verifying devOnly segments are skipped - Command palette and sidebar nav already excluded devOnly routes - Playwright smoke tests unchanged (URLs preserved) --- .../dev-errors/boundary-error/page.tsx | 7 ------- src/app/dashboard/dev-errors/layout.tsx | 19 +++++++++++++++++++ src/app/dashboard/dev-errors/page.tsx | 7 ------- .../dashboard/dev-errors/route-error/page.tsx | 8 -------- src/lib/routeMetadata.test.ts | 8 ++++++++ src/lib/routeMetadata.tsx | 9 +++++++++ 6 files changed, 36 insertions(+), 22 deletions(-) create mode 100644 src/app/dashboard/dev-errors/layout.tsx diff --git a/src/app/dashboard/dev-errors/boundary-error/page.tsx b/src/app/dashboard/dev-errors/boundary-error/page.tsx index 0637979..ba132b2 100644 --- a/src/app/dashboard/dev-errors/boundary-error/page.tsx +++ b/src/app/dashboard/dev-errors/boundary-error/page.tsx @@ -1,13 +1,6 @@ -import { notFound } from "next/navigation"; import TriggerBoundaryError from "./TriggerBoundaryError"; -const DEV_ERRORS_ENABLED = process.env.NODE_ENV !== "production"; - export default function DashboardBoundaryErrorPage() { - if (!DEV_ERRORS_ENABLED) { - notFound(); - } - return (

diff --git a/src/app/dashboard/dev-errors/layout.tsx b/src/app/dashboard/dev-errors/layout.tsx new file mode 100644 index 0000000..0a21554 --- /dev/null +++ b/src/app/dashboard/dev-errors/layout.tsx @@ -0,0 +1,19 @@ +import { notFound } from "next/navigation"; +import type { ReactNode } from "react"; + +const DEV_ERRORS_ENABLED = process.env.NODE_ENV !== "production"; + +export default function DevErrorsLayout({ children }: { children: ReactNode }) { + if (!DEV_ERRORS_ENABLED) { + notFound(); + } + + return ( +
+

+ Internal — dev-only route +

+ {children} +
+ ); +} diff --git a/src/app/dashboard/dev-errors/page.tsx b/src/app/dashboard/dev-errors/page.tsx index d6d59d3..2646998 100644 --- a/src/app/dashboard/dev-errors/page.tsx +++ b/src/app/dashboard/dev-errors/page.tsx @@ -1,13 +1,6 @@ import Link from "next/link"; -import { notFound } from "next/navigation"; - -const DEV_ERRORS_ENABLED = process.env.NODE_ENV !== "production"; export default function DashboardDevErrorsPage() { - if (!DEV_ERRORS_ENABLED) { - notFound(); - } - return (
diff --git a/src/app/dashboard/dev-errors/route-error/page.tsx b/src/app/dashboard/dev-errors/route-error/page.tsx index d596d51..a33440a 100644 --- a/src/app/dashboard/dev-errors/route-error/page.tsx +++ b/src/app/dashboard/dev-errors/route-error/page.tsx @@ -1,11 +1,3 @@ -import { notFound } from "next/navigation"; - -const DEV_ERRORS_ENABLED = process.env.NODE_ENV !== "production"; - export default function DashboardRouteErrorPage() { - if (!DEV_ERRORS_ENABLED) { - notFound(); - } - throw new Error("Intentional dashboard route error for boundary testing"); } diff --git a/src/lib/routeMetadata.test.ts b/src/lib/routeMetadata.test.ts index 737d554..a92b434 100644 --- a/src/lib/routeMetadata.test.ts +++ b/src/lib/routeMetadata.test.ts @@ -200,3 +200,11 @@ test("breadcrumbs for docs/tokens path are correct", () => { ["Home", "Docs", "Design Tokens"], ); }); + +test("breadcrumbs skip devOnly routes", () => { + const breadcrumbs = buildBreadcrumbsFromPath("/dashboard/dev-errors/boundary-error"); + const labels = breadcrumbs.map((b) => b.label); + assert.ok(!labels.includes("Dev Errors"), "dev-errors should not appear in breadcrumbs"); + assert.ok(!labels.includes("Boundary Error"), "boundary-error should not appear in breadcrumbs"); + assert.deepEqual(labels, ["Home", "Dashboard"]); +}); diff --git a/src/lib/routeMetadata.tsx b/src/lib/routeMetadata.tsx index 8e95b7e..6f5056a 100644 --- a/src/lib/routeMetadata.tsx +++ b/src/lib/routeMetadata.tsx @@ -240,6 +240,10 @@ export function getRouteLabel(pathname: string, fallback = "Dashboard"): string export function buildBreadcrumbsFromPath( pathname: string, ): import("@/types/breadcrumb.types").BreadcrumbItem[] { + const devOnlyHrefs = new Set( + appRouteDefinitions.filter((d) => d.devOnly).map((d) => d.href), + ); + const segments = pathname.split("/").filter(Boolean); const items: import("@/types/breadcrumb.types").BreadcrumbItem[] = [ { label: "Home", href: "/", icon: routeMetadata["/"]?.icon }, @@ -248,6 +252,11 @@ export function buildBreadcrumbsFromPath( let cumulative = ""; segments.forEach((seg, idx) => { cumulative += `/${seg}`; + + if (devOnlyHrefs.has(cumulative)) { + return; + } + const meta = routeMetadata[cumulative]; items.push({ label: meta?.label ?? seg.charAt(0).toUpperCase() + seg.slice(1),