diff --git a/ui/src/components/Layout.tsx b/ui/src/components/Layout.tsx index 25fd0e3..c8d5de4 100644 --- a/ui/src/components/Layout.tsx +++ b/ui/src/components/Layout.tsx @@ -35,6 +35,9 @@ export function Layout() { const isVendor = scopes.has('platform:admin'); const isTenantAdmin = scopes.has('tenant:manage'); + const onVendorRoute = location.pathname.startsWith('/vendor'); + // Vendor on vendor routes: show only TENANTS. On tenant routes: show tenant portal too (for debugging). + const showTenantPortal = isTenantAdmin && (!isVendor || !onVendorRoute); // Determine current org slug for server dashboard link const currentOrg = organizations.find((o) => o.id === currentOrgId); @@ -68,8 +71,8 @@ export function Layout() { )} - {/* Tenant portal — only visible to tenant admins (tenant:manage scope) */} - {isTenantAdmin && ( + {/* Tenant portal — visible to tenant admins; hidden for vendor on vendor routes */} + {showTenantPortal && ( <> } diff --git a/ui/src/router.tsx b/ui/src/router.tsx index 7fd04da..da6591b 100644 --- a/ui/src/router.tsx +++ b/ui/src/router.tsx @@ -22,6 +22,12 @@ function LandingRedirect() { const { organizations, currentOrgId } = useOrgStore(); const currentOrg = organizations.find((o) => o.id === currentOrgId); + // Wait for scopes to be resolved — they're loaded async by OrgResolver. + // An empty set means "not yet loaded" (even viewer gets observe:read). + if (scopes.size === 0) { + return null; // OrgResolver is still fetching tokens + } + // Vendor → vendor console if (scopes.has('platform:admin')) { return ;