V1 Web PWA · Surface walkthrough

Tenant Invite

A per-surface walkthrough of the V1 Web PWA customer surface: layout, states, interactions, data, and cross-references.

walked
8sections2 minread

On this page

Context. surface customer · domain onboarding-tenant · route /tenant/invite · auth signed-in · source apps/oshun/web/src/app/tenant/invite/page.tsx

Last walked. 2026-06-26 automated runtime coverage added - Playwright creates a real tenant invitation through the BFF, signs in as the invitee, accepts the token from this page, and reads the tenant-console cockpit state back through the BFF. Outbound email, tenant SSO/MFA, and full onboarding completion remain journey-level gaps.

Purpose#

Signed-in handoff page for a one-time tenant member invitation. It accepts an inviteId + token pair through the tenant invitation BFF and then links the member into onboarding.

Entry Points#

  • Invite URL after authentication: /tenant/invite?inviteId=<inviteId>&token=<token>&tenant=<tenantId>&next=/onboarding
  • Anonymous arrivals are protected by the app proxy and redirect to /welcome?redirect=... before this page renders.
  • next is sanitized by sanitizeRedirectPath; default target is /onboarding.

Layout Regions#

  • LWebShell with LCustomerNav active on Profile.
  • LMasthead titled "Join your tenant." with the tenant id or membership fallback in the right rail.
  • TenantInviteAcceptPanel below the masthead.

States#

  • Ready - invite id and token are present; the accept button is enabled.
  • Submitting - the accept button is disabled while the BFF request is in flight.
  • Accepted - renders the accepted member id, tenant id, status, roles, and a link to the sanitized next route.
  • Missing - missing invite id or token disables submit and reports the incomplete link state.
  • Error - maps BFF status codes to operator-readable invite messages: unauthenticated, token mismatch, already used/revoked, expired, or incomplete.

Interactions#

  • Accept invitation
    • Selector: [data-tenant-invite-accept]
    • BFF: POST /v1/admin/tenant-console/members/invite/:inviteId/accept
    • Body: { token }
    • On success, the panel moves to data-tenant-invite-status="accepted".
  • Continue to onboarding
    • Selector: [data-tenant-invite-next]
    • Target: sanitized next query param, default /onboarding.

Data & Contracts#

  • Reads: query params inviteId, token, tenant, and next.
  • Writes:
    • api.post('/v1/admin/tenant-console/members/invite/:inviteId/accept', { token })
    • The BFF creates or activates the member under the invite's tenant and marks the invitation accepted.
  • Auth/role check: signed-in customer session. The BFF uses the current authenticated user as the accepted member.
  • Post-condition proof: Playwright reads GET /v1/tenant-console/members?tenantId=<tenantId> with an admin bearer and asserts the active member, accepted invitation, hidden token hash, role, and seat allocation.

E2E Coverage#

Spec: apps/oshun/web/e2e/tenant-member-actions.spec.ts

Coverage depth: partial. The browser page, protected anonymous redirect, live BFF accept, and tenant-console read-back are covered. The outbound email that generates the link, tenant SSO/MFA, full OnboardingWizard completion, first tenant action, and tenant audit reconciliation remain journey-level gaps.

Cross-References#