Tenant Admin Console · Surface walkthrough

Tenant home

A per-surface walkthrough of the Tenant Admin Console tenant-admin surface: layout, states, interactions, data, and cross-references.

drafted
8sections3 minread

On this page

Context. surface tenant-admin · domain meta · route / · auth signed-in (tenant) + scope:tenant:admin:{tenantId} (or tenant:admin:*) · source apps/oshun/tenant-admin/src/app/page.tsx

Last walked.

Purpose#

Arrival view for the tenant cockpit: it greets the signed-in tenant admin with their tenant id and lists every tenant-admin workspace (label + primary job) so they can jump into members, identity, policy, audit, and the rest. It is the home workspace defined in @oshun/navigation (OSHUN_TENANT_WORKSPACE_MODEL).

Entry points#

  • Direct URL / (default landing after a tenant session is issued).
  • /handoff "Continue to tenant home →" link (after IdP issues a tenant-scoped session — see handoff.md).
  • /unauthorized/handoff?returnTo=/ chain once a session exists.
  • Shell sidebar Core › Home item (TenantSidebar, shortLabel: 'Home').

Layout regions#

Wrapped in <TenantShell session currentWorkspaceId="home">:

  • Sidebar (TenantSidebar): Oshun brand + "Tenant cockpit" mode, tenant id label, three groups (Core / Governance / Configuration) of workspace links.
  • Header (TenantHeader): tenant · <tenantId> badge › workspace label ("Tenant home"); user id on the right.
  • Main (#tenant-main, data-testid="tenant-home-page"):
    • <h1>Tenant home</h1>
    • Welcome paragraph containing <code data-testid="tenant-home-tenant-id">.
    • <section aria-labelledby="tenant-workspace-grid-heading"> with <h2 id="tenant-workspace-grid-heading">Workspaces</h2> and a <ul data-testid="tenant-workspace-grid"> of all 13 workspace definitions.

States#

  • Loading — N/A; server component renders synchronously from getTenantServerSession() + listOshunTenantWorkspaceDefinitions() (no client fetch, no Suspense boundary).
  • Empty — N/A; the workspace list is sourced from a static model (OSHUN_TENANT_WORKSPACE_IDS, 13 entries) and is never empty.
  • Populated (short) — N/A; the list is always exactly the 13 canonical workspaces.
  • Populated (long) — N/A; fixed-length static list.
  • Error (recoverable) — N/A; no query to fail.
  • Error (unrecoverable) — no in-page handler; a thrown render error surfaces via Next's default error boundary (no custom error.tsx).
  • Offline — N/A as a write/fetch concern; the page is fully server rendered. (verify: no service worker registered for tenant-admin.)
  • Gated — no session → getTenantServerSession() returns nullredirect('/unauthorized?reason=missing-session&returnTo=/'). Middleware redirects unauthenticated requests first (defense-in-depth duplicate).
  • Standalone PWA — N/A; layout.tsx sets robots: { index: false } and a viewport but ships no manifest/installability. (verify.)

Interactions#

  • Workspace list items (<li data-workspace-id={definition.id}>)
    • Function: render-only — each shows <strong>{label}</strong> — {primaryJob} for all 13 workspaces. The list items are NOT links (navigation links live in the sidebar; see Cross-references).
    • Keyboard: not focusable (plain text).
    • Screen reader: announced as list items under the "Workspaces" heading.
    • Touch target: N/A (non-interactive).
    • Mobile (≤ 640 px): inherits TenantShell layout.
    • Disabled when: N/A.
    • Offline behavior: N/A (static).
    • Telemetry: None.
  • Shell-level interactions (sidebar links, skip link) are documented in Cross-references and belong to TenantShell.

Data & contracts#

  • Reads: getTenantServerSession() (cookie oshun-tenant-session, parsed by parseTenantSessionToken) → TenantServerSession; listOshunTenantWorkspaceDefinitions() from @oshun/navigationOshunTenantWorkspaceDefinition[]. No BFF call.
  • Writes: None.
  • Realtime: None.
  • Caching: SSR (dynamic — reads request cookies via next/headers).
  • Auth/role check: middleware.ts (redirects to /unauthorized when the cookie is missing/invalid) + page-level getTenantServerSession() which calls canEnterTenantShell(scopes, tenantId) — requires tenant:admin:* or tenant:admin:{tenantId}.

Cross-references#

Open questions / known gaps#

  • Sidebar groups in TenantSidebar cover only core, governance, configuration (13 workspaces). Confirm the home-page list (all 13 via listOshunTenantWorkspaceDefinitions) and sidebar grouping stay in sync.
  • No custom error.tsx/not-found.tsx/loading.tsx in src/app — confirm whether Next defaults are acceptable for tenant-admin.
  • List items are non-navigable; verify intent that home is purely an index and all navigation is via the sidebar.