From e93b98814620df06721acf8b1ff0e9f439bea228 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 2 Jul 2026 03:18:42 -0400 Subject: [PATCH] fix: align dashboard guard with fallback menu access --- .../DashboardAccessGuard.boundary.test.ts | 28 +++++++++++++++++++ .../layout/DashboardAccessGuard.tsx | 5 +++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/apps/dashboard/src/components/layout/DashboardAccessGuard.boundary.test.ts b/apps/dashboard/src/components/layout/DashboardAccessGuard.boundary.test.ts index 596542a..c6f2104 100644 --- a/apps/dashboard/src/components/layout/DashboardAccessGuard.boundary.test.ts +++ b/apps/dashboard/src/components/layout/DashboardAccessGuard.boundary.test.ts @@ -66,6 +66,34 @@ describe('DashboardAccessGuard route helpers', () => { ]) }) + it('uses baseline routes when the production menu only contains dashboard and billing recovery links', () => { + expect(resolveAllowedRoutes({ + subscriptionAccessLevel: 'full', + items: [ + { + id: 'dashboard', + itemType: 'INTERNAL_PAGE', + routeOrUrl: '/', + children: [], + }, + { + id: 'subscription', + itemType: 'INTERNAL_PAGE', + routeOrUrl: '/subscription', + children: [], + }, + ], + }, 'OWNER')).toEqual([ + '/', + '/reservations', + '/fleet', + '/customers', + '/reports', + '/billing', + '/settings', + ]) + }) + it('does not apply baseline fallback when subscription access is none', () => { expect(resolveAllowedRoutes({ items: [], subscriptionAccessLevel: 'none' }, 'OWNER')).toEqual([]) }) diff --git a/apps/dashboard/src/components/layout/DashboardAccessGuard.tsx b/apps/dashboard/src/components/layout/DashboardAccessGuard.tsx index ddf05ea..90206a4 100644 --- a/apps/dashboard/src/components/layout/DashboardAccessGuard.tsx +++ b/apps/dashboard/src/components/layout/DashboardAccessGuard.tsx @@ -39,6 +39,8 @@ const BASELINE_MENU_ROUTES = [ { route: '/settings', minRole: 'OWNER' }, ] as const +const MENU_RECOVERY_ROUTES = new Set(['/subscription', '/subscription/success', '/subscription/cancel']) + function hasMinRole(employeeRole: string, minRole: string): boolean { return (ROLE_RANK[employeeRole] ?? 0) >= (ROLE_RANK[minRole] ?? 0) } @@ -65,9 +67,10 @@ export function flattenInternalRoutes(items: GeneratedMenuItem[]): string[] { export function resolveAllowedRoutes(menu: EmployeeMenuResponse, role: string): string[] { const routes = flattenInternalRoutes(menu.items) + const featureRoutes = routes.filter((route) => route !== '/' && !MENU_RECOVERY_ROUTES.has(route)) const shouldUseBaselineFallback = menu.subscriptionAccessLevel !== 'none' && - (routes.length === 0 || routes.every((route) => route === '/')) + featureRoutes.length === 0 return shouldUseBaselineFallback ? getBaselineInternalRoutes(role) : routes }