Skip to main content

Client portal

What this is​

Client portal is the read-only reporting area for client users. It shows the paid acquisition report, including KPI cards, drill-ins, tables, and ad details, for the client tied to the signed-in account. Client users cannot switch the report to another client or edit saved views here.

Common questions​

Why did I land in the admin area instead of the portal?​

Admin-tier users are sent to the admin area. The portal is for client users.

Can I view another client’s data?​

No. The portal is locked to the client connected to your account, even if a different client is entered in the page address.

Does the portal have the same report as the main reporting area?​

Yes. It has the paid acquisition report’s KPI cards, drill-ins, tables, and ad detail views, with client-scope and saved-view editing controls removed.

Can I request an ad be linked?​

Yes. Client users can submit a request to link an ad for an admin to review.

Why does the screenshot show an admin page?​

The screenshot was taken while signed in as an admin user, so the app redirected away from the portal. It does not show the client-user portal view.

How to use it​

  1. Sign in with your client user account.
  2. Open the portal and use the report’s date range and available report controls.
  3. Drill into the KPI cards, tables, or ad details to investigate performance.
  4. Submit an ad-linking request when you need an ad connected to the report.

Screenshot​

Portal (admin bounce)

For developers — routes, guards, and internals
  • Route: /portal
  • Guard: any authenticated user; admin-tier roles (super_admin / admin / account_manager, per isAdminTier) are bounced to /admin (src/app/portal/layout.tsx). No session → /login?callbackUrl=/portal.

The client-facing read-only reporting surface. The screenshot shows the bounce, not the portal: the capture session was super_admin, so visiting /portal redirected through /admin and landed on the Paid Acquisition Report — which is itself the documented behavior for admin-tier users. A true portal capture requires a client_user session (none was available read-only).

Features

  • Admin-tier bounce. The Node-runtime layout guard (the primary gate — the proxy does no role check) redirects any isAdminTier role to /admin; admins manage from the admin surface, clients read from the portal. This is what the screenshot demonstrates.
  • Server-side tenant lock — the tenancy showcase. Scope is derived from the session via resolveQueryScope() (src/lib/auth-scope.ts): a client_user is locked to their own clientSlug and any ?client= query hint is ignored server-side, so this surface can never read another client's data. This is the fail-closed multi-tenant isolation rule from contributor guide made visible.
  • Same report, portal mode. The page renders the exact same MarketingReport component as /marketing with mode="portal" — clients get the full Paid Acquisition Report feature set (KPI cards, drill-in views, tables, drawers) with no duplicated code path.
  • What portal mode removes. The super-admin client scope bar (a client user has exactly one scope) and saved-view editing controls are hidden; everything else behaves as on /marketing.
  • Shared chrome. Uses the same AdminSidebarShell; the sidebar is permission-driven (nav.ts), so a client user naturally sees only the entries their permissions grant — no bespoke portal header exists.
  • Ad-binding requests. The portal is also the origin of ad-binding requests: client users can raise "link this ad" requests through /api/portal/ad-binding-requests, which land in the admin approval queue at /admin/clickup-audit?tab=requests (the former /admin/ad-binding-requests route).
  • Capturing a real portal view. To screenshot the actual client experience, create a client_user on a tenant (/admin/clients/[slug]?tab=users), sign in as them (or mint a magic link targeting that user), and visit /portal — an admin session can never see it because of the bounce.

Key files

KindPath
Layout guardsrc/app/portal/layout.tsx
Pagesrc/app/portal/page.tsx
Report componentsrc/components/marketing/MarketingReport.tsx (mode="portal")
Tenancy chokepointsrc/lib/auth-scope.ts (resolveQueryScope)
Role gatesrc/lib/rbac.ts (isAdminTier)
APISame /api/marketing/* routes as /marketing, scope-locked server-side; /api/portal/ad-binding-requests
Navsrc/lib/nav.ts (permission-driven sidebar; client users see only granted entries)
Shellsrc/components/admin/AdminSidebarShell.tsx